พฤติกรรมที่ไม่คาดหมายกับ echo [[: หลัก:]]


10

ฉันต้องการถาม:

ทำไมecho {1,2,3}ขยายไป 1 2 3 ซึ่งเป็นพฤติกรรมที่คาดหวังในขณะที่echo [[:digit:]]ผลตอบแทน[[:digit:]]ในขณะที่ผมคาดว่ามันจะพิมพ์ตัวเลขทั้งหมดจาก0การ9?


คำตอบ:


34

เพราะมันเป็นสองสิ่งที่แตกต่างกัน {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

$ 

4
ดูเพิ่มเติมshopt -s failglobที่จะได้รับประโยชน์มากขึ้นพฤติกรรมคล้ายกับที่ของเปลือกหอยที่ทันสมัยเช่นหรือzsh fish
Stéphane Chazelas

failglobผมเห็นด้วยกับStéphaneใช้ nullglobอาจทำให้เกิดปัญหาที่ไม่คาดคิดเช่นเมื่อวาง URL ?ที่เกิดขึ้นจะมีที่
Kevin

1
แน่นอนฉันเพียงกล่าวnullglobเพื่อแสดงให้เห็นว่ารูปแบบจะถูกตีความเป็นก้อนกลมโดยเปลือก
terdon

14

{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}

ขอบคุณสำหรับคำตอบของคุณฉันเพิ่งรู้จัก linux ดังนั้นขอให้ฉันถามคุณว่า echo เกี่ยวข้องกับไฟล์ 1 3 ได้อย่างไรฟังก์ชั่นนี้คือพิมพ์อาร์กิวเมนต์ของ stdout ที่ไม่ค้นหาไฟล์ตามความรู้ของฉัน
AbdAllah Talaat

1
@AbdAllahTalaat สิ่งนี้ไม่มีส่วนเกี่ยวข้องกับเสียงสะท้อน เปลือก (เช่นทุบตี) "จะขยายว่า" [[:digit:]] ก่อนที่จะผ่านไปechoจึงechoไม่เคยเห็นก็เห็นเท่านั้น[[:digit:]] 1 3คุณสามารถเห็นสิ่งนี้ในการดำเนินการโดยการเรียกใช้set -xซึ่งจะพิมพ์คำสั่งจริงที่กำลังทำงานอยู่ (เรียกใช้set +xเพื่อปิดมันอีกครั้ง)
terdon

@AbdAllahTalaat, echoไม่ได้มองหาไฟล์, เปลือกechoไม่ก่อนที่จะรัน
ilkkachu

โดยเฉพาะอย่างยิ่งเมื่อฉันคิดว่าใน DOS / Windows ยูทิลิตี้จะขยายสัญลักษณ์แทนไม่ใช่เชลล์ (ฉันอาจผิด)
ilkkachu

คนที่ขอโทษฉันเปลี่ยนคำตอบที่ถูกต้องให้กับคำตอบของ tedron เพราะความคิดเห็นของเขามีความหมายว่าทุบตีคือสิ่งที่ทำงานไม่ได้สะท้อน ... คำตอบของเขามีความหมายเช่นนั้นด้วย ... คุณช่วยฉัน ... ฉันอยากจะใส่ คำตอบที่ถูกต้องสำหรับคำตอบและความคิดเห็นทั้งหมดของคุณ
AbdAllah Talaat

4

{1,2,3}(และเช่น{1..3}เป็นการขยายรั้งพวกเขาจะถูกตีความโดยเชลล์ก่อนที่จะดำเนินการคำสั่ง

[[:digit:]]เป็นโทเค็นการจับคู่รูปแบบแต่คุณไม่ได้ใช้ในตำแหน่งที่มีไฟล์ใด ๆ ที่ตรงกับรูปแบบนั้น หากคุณใช้การจับคู่รูปแบบที่ไม่มีการจับคู่จะขยายตัวเอง:

$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3

ไม่ตามที่คำตอบอื่น ๆ ระบุไว้อย่างถูกต้องรูปแบบนั้นถูกจับคู่กับชื่อไฟล์
Toby Speight
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.