awk 'processing_script_here' my=file.txt
ดูเหมือนจะหยุดและรออย่างไม่มีกำหนด ...
เกิดอะไรขึ้นที่นี่และฉันจะทำให้มันทำงานอย่างไร
awk 'processing_script_here' my=file.txt
ดูเหมือนจะหยุดและรออย่างไม่มีกำหนด ...
เกิดอะไรขึ้นที่นี่และฉันจะทำให้มันทำงานอย่างไร
คำตอบ:
ตามที่Chris บอกว่าข้อโต้แย้งของแบบฟอร์มvariablename=anything
นั้นจะถือว่าเป็นการกำหนดตัวแปร (ซึ่งจะดำเนินการในเวลาที่การประมวลผลมีข้อขัดแย้งซึ่งตรงข้ามกับ (ที่ใหม่กว่า) -v var=value
ซึ่งจะดำเนินการก่อนBEGIN
ข้อความ) แทนชื่อไฟล์อินพุต
นั่นอาจเป็นประโยชน์ในสิ่งต่าง ๆ เช่น:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
โดยที่คุณสามารถระบุFS
/ RS
ต่อไฟล์ที่แตกต่างกัน มันยังใช้กันทั่วไปใน:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
เวอร์ชันที่ปลอดภัยกว่าของ:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(ซึ่งใช้งานไม่ได้หากfile1
ว่างเปล่า)
แต่นั่นจะเข้าทางเมื่อคุณมีไฟล์ที่ชื่อมี=
อักขระ
ตอนนี้เป็นเพียงปัญหาเมื่อสิ่งที่เหลืออยู่แรก=
คือawk
ชื่อตัวแปรที่ถูกต้อง
สิ่งที่ถือว่าเป็นชื่อตัวแปรที่ถูกต้องในการเป็นที่เข้มงวดกว่าในawk
sh
POSIX ต้องการให้มันเป็นเช่น:
[_a-zA-Z][_a-zA-Z0-9]*
ด้วยอักขระเท่านั้นของชุดอักขระแบบพกพา อย่างไรก็ตาม/usr/xpg4/bin/awk
อย่างน้อยที่สุดของ Solaris 11 นั้นไม่สอดคล้องกับเรื่องนั้นและอนุญาตให้ตัวอักษรใด ๆ ในสถานที่เกิดเหตุในชื่อตัวแปรไม่ใช่แค่ a-zA-Z
ดังนั้นอาร์กิวเมนต์เช่นx+y=foo
หรือ=bar
หรือ./foo=bar
ยังถือว่าเป็นชื่อไฟล์อินพุตและไม่ใช่การมอบหมายเนื่องจากสิ่งที่เหลืออยู่ของชื่อแรก=
ไม่ใช่ชื่อตัวแปรที่ถูกต้อง ข้อโต้แย้งเช่นStéphane=Chazelas.txt
อาจหรือไม่ขึ้นอยู่กับawk
การนำไปใช้และสถานที่
ด้วยเหตุนี้ด้วย awk แนะนำให้ใช้:
awk '...' ./*.txt
แทน
awk '...' *.txt
เช่นเพื่อหลีกเลี่ยงปัญหาหากคุณไม่สามารถรับประกันชื่อของtxt
ไฟล์จะไม่มี=
ตัวอักษร
นอกจากนี้ระวังว่าข้อโต้แย้งเช่น-vfoo=bar.txt
นี้อาจถูกใช้เป็นตัวเลือกหากคุณใช้:
awk -f file.awk -vfoo=bar.txt
(ใช้ได้awk '{code}' -vfoo=bar.txt
กับรุ่นawk
จาก busybox ก่อน 1.28.0 ดูรายงานข้อผิดพลาดที่เกี่ยวข้อง )
อีกครั้งการใช้./*.txt
งานรอบ ๆ ที่ (ใช้./
คำนำหน้ายังช่วยด้วยไฟล์ที่เรียกว่า-
เป็นอย่างอื่นซึ่งawk
เข้าใจว่าหมายถึงอินพุตมาตรฐานแทน)
นั่นเป็นเหตุผล
#! /usr/bin/awk -f
shebangs ใช้งานไม่ได้จริงๆ ในขณะที่var=value
คนที่สามารถทำงานรอบ ๆ โดยการแก้ไขARGV
ค่า (เพิ่ม./
คำนำหน้า) ในBEGIN
คำสั่ง:
#! /usr/bin/awk -f
BEGIN {
for (i = 1; i < ARGC; i++)
if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i] = "./" ARGV[i]
}
# rest of awk script
ที่จะไม่ช่วยตัวเลือกตามที่พวกเขาเห็นawk
และไม่awk
สคริปต์
ปัญหาหนึ่งที่อาจเกิดขึ้นกับเครื่องสำอางที่ใช้./
คำนำหน้านั้นคือมันสิ้นสุดลงFILENAME
แต่คุณสามารถใช้substr(FILENAME, 3)
เพื่อถอดออกได้หากคุณไม่ต้องการ
การนำ GNU ไปใช้awk
แก้ไขปัญหาเหล่านั้นด้วย-E
ตัวเลือก
หลังจากนั้น-E
gawk คาดว่าจะมีเพียงพา ธ ของawk
สคริปต์ (โดยที่-
ยังคงหมายถึง stdin) จากนั้นจะแสดงรายการของอินพุตไฟล์พา ธ เท่านั้น (และที่นั่นแม้จะไม่ได้-
รับการดูแลเป็นพิเศษ)
มันออกแบบมาเป็นพิเศษสำหรับ:
#! /usr/bin/gawk -E
shebangs ที่รายการอาร์กิวเมนต์เป็นไฟล์อินพุตอยู่เสมอ (โปรดทราบว่าคุณยังคงมีอิสระในการแก้ไขARGV
รายการนั้นในBEGIN
คำสั่ง)
คุณยังสามารถใช้เป็น:
gawk -e '...awk code here...' -E /dev/null *.txt
เราใช้-E
กับสคริปต์ที่ว่างเปล่า ( /dev/null
) เพื่อให้แน่ใจว่าสิ่งเหล่า*.txt
นั้นในภายหลังจะถูกใช้เป็นไฟล์อินพุตแม้ว่าจะมี=
อักขระอยู่ก็ตาม
../foo
, /path/to/foo
และเส้นทางที่อยู่ในการเข้ารหัสที่แตกต่างกัน) - ซึ่งในกรณีนี้substr(FILENAME,3)
จะไม่เพียงพอหรือเป็น สคริปต์ shot หนึ่งที่ผู้ใช้โดยทั่วไปรู้ว่าชื่อไฟล์คืออะไรในกรณีที่ s / เขาอาจไม่ต้องรำคาญกับสิ่งใด ๆ ของพวกเขาที่มี=
;-)
./
เป็นปัญหามาก แต่อาจไม่เป็นที่พึงปรารถนาภายใต้เงื่อนไขบางประการเช่นกรณีที่ชื่อไฟล์จะต้องรวมอยู่ในผลลัพธ์ซึ่งในกรณีนี้./
ควรซ้ำซ้อนและไม่จำเป็นดังนั้นคุณ จะต้องกำจัดมันอย่างใด นี่คือตัวอย่างอย่างน้อยหนึ่งรายการ สำหรับผู้ใช้ที่รู้ว่าชื่อไฟล์คืออะไร - ในกรณีนี้เรายังรู้ว่าชื่อไฟล์คืออะไร แต่=
ก็ยังได้รับการประมวลผลที่เหมาะสม ดังนั้นผู้นำสามารถ-
เข้าไปในทาง
./
คำนำหน้าเพื่อแก้ไขawk
คุณสมบัติ (mis) แต่สุดท้ายคุณก็จบลงด้วยสิ่งที่./
อยู่บนเอาท์พุทซึ่งคุณอาจต้องการตัดออก ดูวิธีตรวจสอบว่าไฟล์บรรทัดแรกมีสตริงที่ระบุหรือไม่? ตัวอย่างเช่น.
./
แต่ยังรวมถึงโกลบอล (พา ธ สัมบูรณ์) /
ซึ่งทำให้ awk ตีความอาร์กิวเมนต์เป็นไฟล์
ในเวอร์ชันส่วนใหญ่ของ awk อาร์กิวเมนต์หลังจากโปรแกรมที่จะดำเนินการมีดังนี้:
x=y
เนื่องจากชื่อไฟล์ของคุณกำลังถูกตีความว่าเป็นกรณีที่ # 2 awk ยังคงรอให้บางสิ่งบางอย่างอ่านบน stdin (เนื่องจากไม่เข้าใจว่ามีการผ่านชื่อไฟล์ใด ๆ )
พฤติกรรมนี้มีการบันทึกไว้ใน POSIX :
อาร์กิวเมนต์สองประเภทต่อไปนี้สามารถผสมกันได้:
- ไฟล์: ชื่อพา ธ ของไฟล์ที่มีอินพุตที่จะอ่านซึ่งจับคู่กับชุดรูปแบบในโปรแกรม หากไม่มีการระบุตัวถูกดำเนินการไฟล์หรือหากตัวถูกดำเนินการไฟล์เป็น '-' จะต้องใช้อินพุตมาตรฐาน
- การมอบหมาย: ตัวถูกดำเนินการที่ขึ้นต้นด้วยตัวอักษรขีดล่างหรือตัวอักษรจากชุดอักขระแบบพกพา (ดูตารางในปริมาณคำจำกัดความฐานของ IEEE Std 1003.1-2001, ส่วน 6.1, ชุดอักขระแบบพกพา) ตามด้วยลำดับขีดล่างตัวเลข และตัวอักษรจากชุดอักขระแบบพกพาตามด้วยอักขระ '=' ต้องระบุการมอบหมายตัวแปรแทนชื่อพา ธ
ด้วยเหตุนี้คุณจึงมีตัวเลือกน้อย (# 1 น่าจะเป็นสิ่งที่รบกวนน้อยที่สุด):
awk ... ./my=file
ซึ่งก้าวเท้าข้างนี้เนื่องจาก.
ไม่ใช่ "ตัวอักษรขีดล่างหรือตัวอักษรจากชุดอักขระแบบพกพา"awk ... < my=file
ใช้ อย่างไรก็ตามมันใช้งานไม่ได้กับหลายไฟล์ln my=file my_file
แล้วใช้my_file
ตามปกติ จะไม่มีการคัดลอกและไฟล์ทั้งสองจะได้รับการสำรองข้อมูลเดียวกันและข้อมูลเมตาของไอโหนด หลังจากใช้งานแล้วการลบลิงก์ที่สร้างขึ้นเนื่องจากจำนวนการอ้างอิงไปยัง inode จะยังคงมีค่ามากกว่า 0./my=file
? % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
สิ่งนี้ควรพกพาได้เพราะ./my
ไม่ใช่ชื่อตัวแปรที่ถูกต้องดังนั้นจึงไม่ควรแยกวิเคราะห์ด้วยวิธีนี้
=
จะนำหน้าด้วยการขีดเส้นใต้หรือตัวอักษรตัวอักษรจากชุดตัวอักษรแบบพกพา (ดูตารางในปริมาณฐานความหมายของ IEEE Std 1,003.1-2,001 มาตรา 6.1 ชุดอักขระแบบพกพา) ตามลำดับของขีดตัวเลขและ alphabetics จากชุดตัวอักษรแบบพกพา ดังนั้นเส้นทางของไฟล์เช่น++foo=bar.txt
หรือ=foo
หรือ./foo=bar
จะ OK ทุกที่.
หรือไม่ได้เป็น+
[_a-zA-Z]
./my=file
จะถูกส่งผ่านคำต่อคำ
awk '{print $1,$2}' /etc/passwd
สำหรับเดียวกัน ประเด็นก็คือว่าการที่เชลล์เปิดไฟล์นั้นตรงข้ามกับ awk นั้นไม่ได้สร้างความแตกต่างว่ามันทำให้มันค้นหาได้หรือไม่ ในความเป็นจริงawk '{exit}' < /etc/passwd
คุณคาดหวังที่awk
จะหาทางกลับไปยังจุดสิ้นสุดของการบันทึกแรกexit
เพื่อให้แน่ใจว่ามันออกจากตำแหน่งภายใน stdin ที่นั่น POSIX ต้องการสิ่งนั้น /usr/xpg4/bin/awk
ไม่ก็บน Solaris แต่ไม่gawk
ว่ามิได้mawk
ดูเหมือนจะทำมันใน GNU / Linux
awk
วิธีนั้น
ในการอ้างเอกสาร gawk (เพิ่มการเน้นข้อความ):
อาร์กิวเมนต์เพิ่มเติมใด ๆ ในบรรทัดคำสั่งจะถือว่าเป็นไฟล์อินพุตที่ต้องดำเนินการตามลำดับที่ระบุ อย่างไรก็ตามอาร์กิวเมนต์ที่มีรูปแบบ var = value กำหนดค่าให้กับตัวแปร var ซึ่งไม่ได้ระบุไฟล์เลย
เหตุใดคำสั่งจึงหยุดและรอ เพราะในรูปแบบawk 'processing_script_here' my=file.txt
ไม่มีไฟล์ที่ระบุโดยคำนิยามข้างต้น - my=file.txt
ถูกตีความว่าเป็นการกำหนดตัวแปรและหากไม่มีไฟล์ที่กำหนดไว้awk
จะอ่าน stdin (ยังเห็นได้ชัดจากการstrace
ที่แสดงให้เห็นว่า awk ในคำสั่งดังกล่าวกำลังรอread(0,'...)
syscall
นี่คือเอกสารในPOSIX awk สเปคดูส่วน OPERANDS และส่วนหนึ่งของการกำหนด )
การกำหนดตัวแปรมีความชัดเจนในawk '{print foo}' foo=bar /etc/passwd
ค่าที่foo
ถูกพิมพ์สำหรับทุกบรรทัดใน / etc / passwd การระบุ./foo=bar
หรือเส้นทางแบบเต็มใช้งานได้
โปรดทราบว่าการทำงานstrace
บนawk '1' foo=bar
เช่นเดียวกับการตรวจสอบกับcat foo=bar
แสดงให้เห็นว่ามีปัญหานี้ awk เฉพาะและ execve ไม่แสดงชื่อไฟล์เป็นอาร์กิวเมนต์ผ่านเพื่อให้เปลือกหอยมีอะไรจะทำอย่างไรกับการกำหนดตัวแปร env ในกรณีนี้
นอกจากนี้โปรดทราบว่าawk '...script...' foo=bar
จะไม่ทำให้เกิดการสร้างตัวแปรสภาพแวดล้อมโดยเชลล์เนื่องจากการกำหนดค่าตัวแปรสภาพแวดล้อมควรอยู่ก่อนหน้าคำสั่งเพื่อให้มีผลบังคับใช้ ดูPOSIX กฎไวยากรณ์ของเชลล์หมายเลข 7 นอกจากนี้ยังสามารถตรวจสอบผ่านawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd