ฉันจะป้องกันการขยาย Bash จากการส่งไฟล์ที่ขึ้นต้นด้วย“ -” เป็นอาร์กิวเมนต์ได้อย่างไร


14

ฉันพยายามค้นหาสตริงซ้ำด้วยซ้ำgrepแต่ฉันได้รับสิ่งนี้:

$ grep -r "stuff" *
grep: unrecognized option '---corporate-discount.csv'
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.

ฉันจะป้องกัน Bash ไม่ให้ส่งไฟล์ที่ขึ้นต้นด้วย-อาร์กิวเมนต์ได้อย่างไร



3
คุณไม่ต้องการป้องกันเชลล์จากการส่งไฟล์เหล่านี้ใช่ไหม? คำถามค่อนข้างจะบอกได้อย่างไรgrepว่าพวกเขาไม่ใช่ตัวเลือก
DonHolgo

11
... เพื่อให้ชัดเจน bash ไม่ได้ควบคุมว่าผลลัพธ์ใดที่ถือว่าเป็นตัวเลือกและถือว่าเป็นอาร์กิวเมนต์ ที่อยู่ในการควบคุมของโปรแกรมรับ คุณจะได้รับพฤติกรรมเดียวกันกับsubprocess.Popen(['grep', '-r', '-e' 'stuff', '--corporate-discount.csv'])ใน Python ไม่มีทุบตีใด ๆ
Charles Duffy

1
การอ่านที่เกี่ยวข้อง: Unix Wildcards Gone Wildเกี่ยวกับปัญหาด้านความปลอดภัยที่อาจเกิดจากการใช้*คำสั่ง สิ่งเหล่านี้สามารถหลีกเลี่ยงได้โดยใช้./*แทน
Wildcard

1
@ Wildcard, การใช้--เป็นจุดสิ้นสุดของตัวเลือก sigil ก็สมเหตุสมผลเช่นกัน; แนวทางไวยากรณ์ของยูทิลิตี้ POSIXนั้นจำเป็นต้องได้รับการยอมรับ ดูแนวทาง # 10 (แน่นอนว่าไม่ใช่ทุกโปรแกรมที่ทำตามแนวทาง POSIX แต่คำตอบคือการรวมโปรแกรมของผู้เขียนโปรแกรมและ / หรือนำออกจากอุตสาหกรรม)
Charles Duffy

คำตอบ:


43

ก่อนอื่นให้สังเกตว่าการตีความข้อโต้แย้งที่ขึ้นต้นด้วยขีดกลางขึ้นอยู่กับโปรแกรมที่กำลังเริ่มต้นgrepหรืออื่น ๆ เปลือกไม่มีวิธีการควบคุมโดยตรง

สมมติว่าคุณต้องการประมวลผลไฟล์ดังกล่าว (และไม่สนใจไฟล์เหล่านั้นทั้งหมด) grepรวมถึงโปรแกรมส่วนใหญ่ให้จดจำ--เป็นการระบุตัวเลือกสุดท้ายของตัวเลือกดังนั้น

grep -r -e "stuff" -- *

จะทำในสิ่งที่คุณต้องการ -eมีในกรณีที่stuffเริ่มต้นด้วย-เช่นกัน

หรือคุณสามารถใช้:

grep -r -e "stuff"  ./*

หนึ่งหลังนั้นก็จะหลีกเลี่ยงปัญหาหากมีไฟล์ที่เรียกว่า-ในไดเรกทอรีปัจจุบัน แม้หลัง--ตัวคั่นgrepตีความ-เป็นความหมาย stdin ในขณะที่./-เป็นไฟล์ที่เรียกว่า-ในไดเรกทอรีปัจจุบัน


8

เพื่อป้องกัน Bash ส่วนขยายผ่านไฟล์ที่ขึ้นต้นด้วย“ -”คุณสามารถใช้:

echo [!-]*

ซึ่งทำงานได้ดีในเชลล์ส่วนใหญ่หรือเฉพาะกับ ksh, bash, zsh:

echo !(-*)

ตัวอย่างเช่น: ในไดเรกทอรีที่มีไฟล์นี้

$ echo *
a b c ---corporate-discount.csv d -e --option.txt

จะแสดงรายการเท่านั้น (หากมีให้extglobใช้งานอยู่):

$ shopt -s extglob
$ echo !(-*)
a b c d

$ echo [!-]*
a b c d

แต่ถ้าสิ่งที่คุณต้องการคือการประมวลผลไฟล์ทั้งหมดในขณะที่บอก grep เพื่อหลีกเลี่ยงการตีความไฟล์ที่ระบุด้วย-ตัวเลือกเป็นเพียงแค่เพิ่ม./:

grep -r "stuff" ./*

หรือหากมีการรับประกันว่าไม่มีไฟล์ที่เรียกว่า-มีอยู่จริงในไฟล์ที่อยู่ในรายการ (grep จะแปลความเหงา-ว่าอ่านจาก stdin ) คุณสามารถใช้:

grep -r -- "stuff" *

ใช่เนื่องจากคำถามเกี่ยวกับ bash เป็นเชลล์ GNU ดูเหมือนว่ามีเหตุผลที่จะสมมติว่ามี grep GNU พร้อมใช้งานสามารถติดตั้งได้หรือกำลังใช้งานจริง @ StéphaneChazelas
Isaac

ใช่ a grep -r -- stuff *นั้นง่ายกว่าและทำงานกับ greps ที่ไม่ใช่ของ GNUish ได้เช่นกัน ดังนั้น: เพิ่มขอบคุณ @ StéphaneChazelas
Isaac

@Isaac ฉันจะไม่พูดว่ามันเป็นสมมติฐานที่สมเหตุสมผล "ถ้า bash พร้อมใช้งานก็สามารถใช้ grep ของ GNU ได้" ยกตัวอย่างเช่น FreeBSD: bash ไม่ได้ถูกติดตั้งตามค่าเริ่มต้นซึ่งสามารถติดตั้งได้ในภายหลัง แต่ไม่มีผลต่อ grep - มันยังคงเป็น grep รุ่น BSD ยกเว้นว่ามีการติดตั้ง GNU grep ไว้อย่างชัดเจน แต่นั่นเป็น nitpick เล็กน้อย ผมชอบวิธีทางเลือกผ่าน extglob จึง +1 คำตอบ
Sergiy Kolodyazhnyy

2
@SergiyKolodyazhnyy, AFAIK, grepบน FreeBSD ยังคงใช้ GNU grepและยังคงมีความผิดพลาดที่ตัวเลือกจะได้รับการยอมรับหลังจากตัวเลือกที่ไม่ใช่ตัวเลือก แม้แต่ BSD เช่น OpenBSD ที่เขียนใหม่grepทำให้ GNU สามารถใช้งานร่วมกับการพกพาแบบย้อนหลังได้ (และยังคงแสดงพฤติกรรมดังกล่าวที่นี่) บน macOS sh คือทุบตี แต่ฉันคาดหวังว่า grep ของพวกเขาจะไม่แสดงพฤติกรรมดังกล่าวเนื่องจาก macOS นั้นมีความสอดคล้องกับ POSIX แม้ว่าจะไม่มี $ POSIXLY_CORRECT ก็ตาม ในกรณีใด ๆ grep OP ที่เป็น GNU เข้ากันได้เพราะจะทำให้ข้อผิดพลาดที่
Stéphane Chazelas

1
ดูยังecho [!-]*เป็นเทียบเท่ามาตรฐานของ ksh (หรือ's)bash -O extglob echo !(-*)
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.