ฉันต้องการถาม:
ทำไม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