ฉันต้องการถาม:
ทำไมecho {1,2,3}ขยายไป 1 2 3 ซึ่งเป็นพฤติกรรมที่คาดหวังในขณะที่echo [[:digit:]]ผลตอบแทน[[:digit:]]ในขณะที่ผมคาดว่ามันจะพิมพ์ตัวเลขทั้งหมดจาก0การ9?
ฉันต้องการถาม:
ทำไมecho {1,2,3}ขยายไป 1 2 3 ซึ่งเป็นพฤติกรรมที่คาดหวังในขณะที่echo [[:digit:]]ผลตอบแทน[[:digit:]]ในขณะที่ผมคาดว่ามันจะพิมพ์ตัวเลขทั้งหมดจาก0การ9?
คำตอบ:
เพราะมันเป็นสองสิ่งที่แตกต่างกัน {1,2,3}เป็นตัวอย่างของการขยายตัวรั้ง การ{1,2,3}สร้างถูกขยายโดยเชลล์ก่อนที่echoจะเห็น คุณสามารถดูว่าเกิดอะไรขึ้นถ้าคุณใช้set -x:
$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3
อย่างที่คุณเห็นคำสั่งecho {1,2,3}ถูกขยายเป็น:
echo 1 2 3
แต่[[:digit:]]เป็นตัวละครคลาส POSIX เมื่อคุณได้ให้มันechoเปลือกยังประมวลผลมันเป็นครั้งแรก แต่คราวนี้มันจะถูกประมวลผลเป็นglob เปลือก มันทำงานในลักษณะเดียวกับที่คุณเรียกใช้echo *ซึ่งจะพิมพ์ไฟล์ทั้งหมดในไดเรกทอรีปัจจุบัน แต่[[:digit:]]เป็นกะลากลมที่จะตรงกับตัวเลขใด ๆ ตอนนี้ในทุบตีหาก shell glob ไม่ตรงกับสิ่งใดมันจะถูกขยายออกไปเอง:
$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files
หาก glob ตรงกับสิ่งที่จะถูกพิมพ์:
$ echo /e*c
+ echo /etc
/etc
ในทั้งสองกรณีechoเพียงแค่พิมพ์สิ่งที่เชลล์บอกให้พิมพ์ แต่ในกรณีที่สองเนื่องจาก glob จับคู่บางสิ่ง ( /etc) มันถูกบอกให้พิมพ์สิ่งนั้น
ดังนั้นเนื่องจากคุณไม่มีไฟล์หรือไดเรกทอรีที่มีชื่อประกอบด้วยหนึ่งหลัก (ซึ่งเป็นสิ่งที่[[:digit:]]จะจับคู่), glob จะขยายตัวเองและคุณจะได้รับ:
$ echo [[:digit:]]
[[:digit:]]
ตอนนี้ลองสร้างไฟล์ชื่อ5และเรียกใช้คำสั่งเดียวกัน:
$ echo [[:digit:]]
5
และหากมีไฟล์ที่ตรงกันมากกว่าหนึ่งไฟล์:
$ touch 1 5
$ echo [[:digit:]]
1 5
นี่คือ (เรียงลำดับ) บันทึกไว้man bashในคำอธิบายของnullglobตัวเลือกที่ปิดการทำงานนี้:
nullglob
If set, bash allows patterns which match no files (see
Pathname Expansion above) to expand to a null string,
rather than themselves.
หากคุณตั้งค่าตัวเลือกนี้:
$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]] ## prints nothing
$
shopt -s failglobที่จะได้รับประโยชน์มากขึ้นพฤติกรรมคล้ายกับที่ของเปลือกหอยที่ทันสมัยเช่นหรือzsh fish
failglobผมเห็นด้วยกับStéphaneใช้ nullglobอาจทำให้เกิดปัญหาที่ไม่คาดคิดเช่นเมื่อวาง URL ?ที่เกิดขึ้นจะมีที่
nullglobเพื่อแสดงให้เห็นว่ารูปแบบจะถูกตีความเป็นก้อนกลมโดยเปลือก
{1,2,3}คือการขยายรั้งมันขยายไปถึงคำที่ระบุไว้โดยไม่คำนึงถึงความหมายของพวกเขา
[...]เป็นกลุ่มตัวอักษรที่ใช้ในการขยายตัวของชื่อไฟล์ (หรือตัวแทนหรือ glob) คล้ายกับเครื่องหมายดอกจันและเครื่องหมายคำถาม* ?มันตรงกับตัวละครเดียวใด ๆ ที่ระบุไว้ภายในหรือตัวละครที่เป็นสมาชิกของกลุ่มที่มีชื่อเช่น[:digit:]ถ้ามีการระบุไว้ พฤติกรรมเริ่มต้นของเชลล์ส่วนใหญ่คือการปล่อยไวด์การ์ดให้เหมือนเดิมหากไม่มีไฟล์ที่ตรงกับมัน
(โปรดทราบว่าคุณไม่สามารถเปลี่ยน wildcard / pattern เป็นชุดของสตริงที่จะจับคู่ได้เครื่องหมายดอกจันสามารถจับคู่สตริงที่มีความยาวใด ๆ ได้ดังนั้นการขยายรูปแบบใด ๆ
ดังนั้น:
$ bash -c 'echo [[:digit:]]' # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]' # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]' # now there are two matches
1 3 # note that d, i, g and t do NOT match
แต่ยังคง:
$ bash -c 'echo {1,2,3}'
1 2 3
ทั้งของผู้ที่มีการขยายตัวเปลือกมันไม่สำคัญว่าถ้าคำสั่งที่คุณกำลังทำงานเป็นlsหรือหรือecho rmโปรดทราบด้วยว่าหากมีการเสนอราคาอย่างใดอย่างหนึ่งจะไม่ถูกขยาย:
$ bash -c 'echo "[[:digit:]]"' # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
[[:digit:]] ก่อนที่จะผ่านไปechoจึงechoไม่เคยเห็นก็เห็นเท่านั้น[[:digit:]] 1 3คุณสามารถเห็นสิ่งนี้ในการดำเนินการโดยการเรียกใช้set -xซึ่งจะพิมพ์คำสั่งจริงที่กำลังทำงานอยู่ (เรียกใช้set +xเพื่อปิดมันอีกครั้ง)
echoไม่ได้มองหาไฟล์, เปลือกechoไม่ก่อนที่จะรัน
{1,2,3}(และเช่น{1..3}เป็นการขยายรั้งพวกเขาจะถูกตีความโดยเชลล์ก่อนที่จะดำเนินการคำสั่ง
[[:digit:]]เป็นโทเค็นการจับคู่รูปแบบแต่คุณไม่ได้ใช้ในตำแหน่งที่มีไฟล์ใด ๆ ที่ตรงกับรูปแบบนั้น หากคุณใช้การจับคู่รูปแบบที่ไม่มีการจับคู่จะขยายตัวเอง:
$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3