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ชื่อตัวแปรที่ถูกต้อง
สิ่งที่ถือว่าเป็นชื่อตัวแปรที่ถูกต้องในการเป็นที่เข้มงวดกว่าในawksh
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ตัวเลือก
หลังจากนั้น-Egawk คาดว่าจะมีเพียงพา ธ ของ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