gawk inplace และ stdout


10

มันเป็นไปได้ที่จะใช้gawkของ-i inplaceตัวเลือกและพิมพ์สิ่งที่stdout?

ตัวอย่างเช่นถ้าฉันต้องการอัปเดตไฟล์และหากมีการเปลี่ยนแปลงใด ๆ ให้พิมพ์ชื่อไฟล์และบรรทัดที่เปลี่ยนแปลงไปที่stderrฉันสามารถทำสิ่งต่าง ๆ เช่น

find -type f -name 'myfiles' -exec gawk -i inplace '{if(gsub(/pat/, "repl")) { print FILENAME > "/proc/self/fd/2" ; print > "/proc/self/fd/2"; } print;}' {} +

แต่มีวิธีใช้stdoutแทนหรือวิธีที่สะอาดกว่าในการพิมพ์บล็อกนั้นไปยังสตรีมสำรอง


2
ในกรณีพิเศษนี้คุณอาจต้องการเรียกใช้บางอย่างเช่นfind -type f -name 'myfiles' -exec grep -q 'pattern' {} \; -print -exec gawk -i inplace '{do_your_sub_and_print_to_dev/stderr_too}' {} \;นั้นคุณจะใช้ awk เพื่อแก้ไขไฟล์ที่มีบรรทัดที่ตรงกับรูปแบบนั้นเท่านั้น
don_crissti

@don_crissti ฉันมักจะลืมเร็วเท่าใดgrepสามารถ สัญชาตญาณแรกของฉันคือการหลีกเลี่ยงการ "ประมวลผล" ไฟล์สองครั้ง แต่ฉันจะไม่แปลกใจเลยถ้ามันเร็วขึ้นแน่นอน มันแสดงให้เห็นว่าทำไมฉันถึงเชื่อในลำไส้ของฉันในการทำโปรไฟล์
Eric Renouf

1
ใช่มันค่อนข้างเร็วและจำไว้ว่า: 1) หากไม่มีการจับคู่คุณจะประมวลผลไฟล์เพียงครั้งเดียว (ส่วนที่-print -exec gawkไม่ได้ดำเนินการอีกต่อไป) และ 2) จะหยุดในการจับคู่ที่ 1 ดังนั้นหากการจับคู่ที่ 1 อยู่บน บรรทัดสุดท้ายคุณยังคงไม่ประมวลผลไฟล์ทั้งหมดสองครั้ง (มากกว่า 1.X เท่า) นอกจากนี้หากgawk -i inplaceทำงานเหมือนsed -iมันจะแก้ไขไฟล์ในสถานที่ต่อไป - เช่นการปรับปรุงการประทับเวลา inode ฯลฯ แม้ว่าจะไม่มีอะไรที่จะแก้ไข ...
don_crissti

คำตอบ:


13

คุณควรใช้/dev/stderrหรือแทน/dev/fd/2 จัดการและด้วยตัวเอง (ไม่ว่าระบบจะมีไฟล์เหล่านั้นหรือไม่ก็ตาม)/proc/self/fd/2gawk/dev/fd/x/dev/stderr

เมื่อคุณทำ:

print "x" > "/dev/fd/2"

gawkทำwrite(2, "x\n")ในขณะที่เมื่อคุณทำ:

print "x" > "/proc/self/fd/2"

เนื่องจากมันไม่ได้ปฏิบัติต่อ/proc/self/fd/xเป็นพิเศษจึงทำ:

fd = open("/proc/self/fd/2", O_WRONLY|O_CREAT|O_TRUNC);
write(fd, "x\n");

อันดับแรก/proc/self/fdคือ Linux เฉพาะและบน Linux พวกเขามีปัญหา ทั้งสองเวอร์ชันข้างต้นจะไม่เทียบเท่าเมื่อ stderr เป็นไฟล์ปกติหรือไฟล์อื่น ๆ ที่ค้นหาได้หรือไปยังซ็อกเก็ต (ซึ่งไฟล์หลังจะล้มเหลว) (ไม่ต้องพูดถึงว่ามันทำให้ไฟล์ descriptor เสีย)

ที่ถูกกล่าวว่าถ้าคุณต้องการที่จะเขียนถึง stdout เดิมคุณต้องบันทึกไว้ใน fd อื่นเช่น:

gawk -i inplace '{
   print "goes into the-file"
   print "to stdout" > "/dev/fd/3"}' the-file 3>&1

gawkจะเปลี่ยนเส้นทาง stdout พร้อมกับแบบแทนที่ไฟล์ มันจำเป็นเพราะเช่นคุณต้องการ:

awk -i inplace '{system("uname")}' file

เพื่อเก็บผลผลิตเข้าสู่unamefile


2

เพียงเพื่อความสนุกสนานวิธีการอื่นโดยใช้เพียง POSIX คุณสมบัติของfind, sh, grepและ (ที่สุด) ex:

find . -type f -name 'myfiles' -exec sh -c '
  for f do grep -q "pat" "$f" &&
    { printf "%s\n" "$f";
      printf "%s\n" "%s/pat/repl/g" x | ex "$f";
  }; done' find-sh {} +

(ตัวแบ่งบรรทัดทั้งหมดในคำสั่งด้านบนเป็นตัวเลือกซึ่งสามารถย่อให้เป็นหนึ่งซับ)

หรือเนื้อหาที่ชัดเจนยิ่งขึ้น:

find . -type f -name 'myfiles' -exec sh -c '
  for f
  do
    if grep -q "pat" "$f"; then
      printf "%s\n" "$f"
      printf "%s\n" "%s/pat/repl/g" x | ex "$f"
    fi
  done' find-sh {} +

:)


(คำสั่งนี้ไม่ได้ทดสอบการแก้ไขยินดีต้อนรับถ้าฉันทำผิดพลาดใด ๆ )

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