Thực hiện thoát bash! = 0 khi được AWK gọi và ngắt với ^ C


7

Tôi đang gặp sự cố với đoạn mã AWK (G) sau:

do {
    ...
} while (system("sleep 10"))

Ý định của tôi là phá vỡ vòng lặp khi người dùng nhấn ^ C trong khi ngủ, nhưng nó không hoạt động.

Tôi tin rằng vấn đề là Bash thoát bằng 0 khi bị gián đoạn với ^ C, ít nhất là khi nó được thực thi bởi AWK system():

$ awk 'BEGIN { print "\n" system("sleep 2") }'
(let the sleep complete)
0

 

$ awk 'BEGIN { print "\n" system("sleep 2") }'
^C
0

Tại sao nó như vậy?

Đây có phải là một lỗi trong Bash hoặc trong (G) AWK không?

Có một giải pháp đơn giản nào không liên quan đến cú pháp cụ thể của Bash như thế trapnào không?

Điều tốt nhất tôi có thể đưa ra là:

do {
    ...
} while (42 == system("sleep 10 && exit 42"))

mà vẫn giống như một cái bùn đối với tôi


1
Bạn có thể xem xét perl cho công việc này, vì bạn có thể rất cụ thể về hành vi xử lý tín hiệu (moreso hơn với ruby ​​hoặc python).
Otheus 3/03/2016

Câu trả lời:


6

Những gì awk system()nên trả lại được chỉ định kém .

Điều có vẻ phổ biến trong các lần awktriển khai là khi thoát bình thường, nó trả về mã thoát (số được chuyển cho exit(3)modulo 256), nhưng khi quá trình shell bị giết bởi tín hiệu, có rất nhiều hành vi khác nhau.

Cũng lưu ý rằng trong khi chức năng C system(3)có nghĩa là để bỏ qua SIGINT (và SIGQUIT) trong các phụ huynh, nó không phải là rất rõ ràng (với tôi ít nhất) mà yêu cầu cũng áp dụng cho awk's system(). Một số awktriển khai (như mawk) sẽ chết vì SIGINT đó (đó cũng là hành vi tôi muốn thấy vì tôi không thích CTRL-C của mình bị bỏ qua chỉ vì awktình cờ chạy system()chức năng), một số (như gawktriển khai truyền thống) sẽ không

Cũng lưu ý rằng một số shell có thể chặn một số tín hiệu đó và cuối cùng gọi exit()sẽ ảnh hưởng đến hành vi (ví dụ như thảo luận trong các bình luận về shell Bourne), đó là lý do tại sao tôi sử dụng exectrong các ví dụ bên dưới để xóa shell khỏi vòng lặp.

Đối với giá trị được trả về bởi system()(thậm chí còn có nhiều biến thể hơn nếu bạn xem xét close()1 ) theo SIGINT, chúng tôi thấy:

$ nawk 'BEGIN {print system("exec kill -s INT $$")}'
0.0078125
$ bwk-awk 'BEGIN {print system("exec kill -s INT $$")}'
0.0078125
$ mawk 'BEGIN {print system("exec kill -s INT $$")}'
130
$ gawk 'BEGIN {print system("exec kill -s INT $$")}'
0

0.0078125trở thành 2 / 256(đối với một SEGVtrong số đó 11, bạn sẽ nhận được 0,542969 ((128 + 11) / 256) nếu một lõi bị đổ, 0,0429688 (11/256) nếu không), nawkđược nawktìm thấy trên Solaris 10 hoặc 11, hoặc cổng Linux của nó trong gia truyền toolchest, bwk-awkđược sự awkduy trì bởi Brian Kernighan mình (các Ktrong awk) cơ sở cho việc awktìm thấy trên một số BSDs (ở đây được thử nghiệm trên Debian GNU / Linux). /usr/xpg4/bin/awktrên Solaris 11 hành xử như thế nào gawk.

Vì vậy, dựa trên giá trị sđược trả về bởi system(3)(một số nguyên nơi bit 0-6 là số tín hiệu, cắn 7 lõi-bit, và bit 8 đến 15 mã thoát), awk's system()lợi nhuận trên một trong hai:

  • s / 256( awkthực hiện truyền thống ),
  • int(s/256)( gawk),
  • hoặc trong mawk, biến đổi tương tự như được thực hiện bởi các shell như Bourne hoặc C-shell chẳng hạn ( (s&127)+128nếu bị giết, s>>8nếu không), ngoại trừ nếu một lõi bị đổ, bạn nhận được (s&127)+256thay vì (s&127)+128(giá trị là (s&255)+128).

Vì vậy, ở đây, bạn có thể làm thay thế:

awk 'BEGIN{print system("trap exit\\ 1 INT; sleep 10")}'

Nhưng nó vẫn awksẽ bị giết với một số awktriển khai như thế nào mawk. Nếu bạn shbashhoặc yash, bạn có thể làm:

awk 'BEGIN{print system("set -m; sleep 10; exit")}'

Vì vậy, sleepđược chạy trong nhóm quy trình riêng của mình (và chỉ có nó mới có SIGINT).

Một cách khác có thể là bỏ qua SIGINT trước khi gọi awk. Tuy nhiên, hầu hết các shell (và đó là yêu cầu POSIX) không thể thay đổi trình xử lý tín hiệu nếu tín hiệu đã bị bỏ qua khi bắt đầu. Vì vậy, những thứ như:

(
  trap '' INT
  awk 'BEGIN{print system("trap exit\\ 1 INT; sleep 10; exit")}'
)

Sẽ không làm việc. zshmặc dù không có giới hạn (tự gây ra), vì vậy nếu bạn biết zshlà có sẵn, bạn có thể làm:

(
  trap '' INT
  awk 'BEGIN{print system("exec zsh -c \"TRAPINT() exit 1; sleep 10\"")}'
)

Điều này sẽ làm việc cho dù awkmawk, gawkhoặc khác và sẽ tránh phải lộn xộn với kiểm soát công việc. Tuy nhiên, tại thời điểm này, sẽ đáng để xem xét sử dụng perl/ python/ ruby... thay vì awknơi bạn có thể điều chỉnh việc xử lý tín hiệu theo nhu cầu của mình.

Ghi chú

1 Khi close()một đường ống, như trong:

awk 'BEGIN {cmd = "kill -s INT $$"; cmd | getline; print close(cmd)}'

Đầu tiên, lần này ^Clàm gián đoạn awktrong tất cả các triển khai tôi đã thử (không có yêu cầu như vậy để bỏ qua SIGINT / SIGQUIT cho popen(3)/ pclose(3)(một cách tự nhiên để thực hiện điều đó getline) như đã có system(3)).

Nhưng khi nói đến trạng thái thoát ( sgiá trị được trả về bởi pclose(3)/ waitpid(2)like cho system()ở trên), chúng ta thấy:

  • Solaris nawk: không hoạt động, bạn không thể gọi close()như thế trong Solaris nawk.
  • /usr/xpg4/bin/awktrên Solaris. Trả về luôn luôn 0, ngay cả khi exit(1)được thực hiện theo quy trình. Rõ ràng là một lỗi phù hợp.
  • gawkbwk-awk: cho s( exit 1cho 256, bị giết bởi SIGINT cho 2, bị giết bởi SIGSEGV là 11 với lõi cho 139).
  • mawk: giống như đối với system(), có vẻ như mawklà triển khai duy nhất đưa ra bất kỳ suy nghĩ nào về điều đó.

Được chỉ định kém hoặc không, nó trông giống như một lỗi trong GAWK đối với tôi. Ồ tốt Cảm ơn
Tobia 3/03/2016

Ngoài ra, set -mbởi chính nó dường như làm các mẹo! system("set -m; sleep 10")thực sự là giải pháp sạch nhất trong nhóm và nó hoạt động đáng tin cậy trên tất cả các AWK. Tuyệt vời!
Tobia 3/03/2016

@Tobia. Vâng, tình cờ làm việc với yashlà tốt. Lưu ý rằng việc sử dụng set -mkhông phải là di động (sẽ không hoạt động với các triển khai khác shmà tôi biết). Và nó sẽ ngăn bạn chạy awktrong nền.
Stéphane Chazelas

Bạn đúng. Nó không hoạt động với Dash, vốn là mặc định /bin/shtrong nhiều hệ thống :-(
Tobia 3/03/2016

Có vẻ như bạn không sử dụng bản gốc nawkmà là một cái gì đó khác.
schily
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.