Làm thế nào để sử dụng nhiều đối số cho awk với một shebang (tức là #!)?


118

Tôi muốn thực hiện một tập lệnh gawk bằng --re-intervalcách sử dụng shebang. Cách tiếp cận "ngây thơ" của

#!/usr/bin/gawk --re-interval -f
... awk script goes here

không hoạt động, vì gawk được gọi với đối số đầu tiên "--re-interval -f"(không được phân tách xung quanh khoảng trắng), mà nó không hiểu. Có giải pháp nào cho điều đó không?

Tất nhiên, bạn có thể không gọi gawk trực tiếp nhưng bọc nó vào một tập lệnh shell tách đối số đầu tiên hoặc tạo một tập lệnh shell sau đó gọi gawk và đặt tập lệnh vào một tệp khác, nhưng tôi đã tự hỏi liệu có cách nào để làm không điều này trong một tệp.

Hành vi của các dòng shebang khác nhau giữa các hệ thống - ít nhất là trong Cygwin, nó không phân chia các đối số bởi các khoảng trắng. Tôi chỉ quan tâm đến việc làm thế nào để thực hiện nó trên một hệ thống hoạt động như vậy; tập lệnh không có nghĩa là di động.


1
Một thử nghiệm ngớ ngẩn mà tôi vừa làm là với một tập lệnh sử dụng một tập lệnh khác trên dòng shebang, nó đã phân chia các đối số một cách chính xác.
Hasturkun

@Hasturkun, điều đó đặt ra một vấn đề khác, rằng hành vi của các dòng shebang cũng khác nhau giữa các hệ thống và wrt hệ thống cho dù bản thân chương trình được gọi có thể là một tập lệnh hay không.
dubiousjim


Với các phiên bản gần đây của gawk (> = 4.0), --re-intervalkhông cần thiết nữa (xem [ gnu.org/software/gawk/manual/… ).

Câu trả lời:


25

Điều này có vẻ hiệu quả với tôi với (g) awk.

#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"


# The real awk program starts here
{ print $0 }

Lưu ý các #!lần chạy /bin/sh, vì vậy tập lệnh này trước tiên được hiểu là tập lệnh shell.

Lúc đầu, tôi chỉ đơn giản là thử "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@", nhưng awk coi đó như một lệnh và in ra mọi dòng đầu vào một cách vô điều kiện. Đó là lý do tại sao tôi đặt vào arbitrary_long_name==0- nó được cho là luôn thất bại. Bạn có thể thay thế nó bằng một số chuỗi vô nghĩa. Về cơ bản, tôi đang tìm kiếm một điều kiện sai trong awk sẽ không ảnh hưởng xấu đến tập lệnh shell.

Trong kịch bản shell, arbitrary_long_name==0định nghĩa một biến được gọi arbitrary_long_namevà đặt nó bằng =0.


Đây là câu trả lời của tôi, nhưng tôi tự hỏi liệu nó có đủ di động và mạnh mẽ hay không. Nó phụ thuộc cụ thể vào bash, hay nó sẽ hoạt động với bất kỳ POSIX shnào? Và tôi không sử dụng awkthường xuyên, vì vậy tôi không chắc thủ thuật của tôi ở dòng thứ hai là một cách tốt để buộc awkbỏ qua dòng.
Aaron McDaid,

Chỉ là những gì tôi đã tự hỏi, +1, nhưng có lẽ không thể dự đoán được (do đó các phiếu bầu tương đối).
Aaron Hall

Bạn có thể giải thích vấn đề này có thể gặp phải không, @AaronHall? Miễn là biến arbitrary_long_namekhông xung đột với biến được sử dụng trong chương trình awk thực, tôi không thể thấy bất kỳ vấn đề nào. Có điều gì tôi đang thiếu?
Aaron McDaid

Sử dụng #!/bin/sh -thay vì #!/bin/shđể bảo vệ tập lệnh có thể hoạt động sai theo cách nguy hiểm nếu được gọi với đối số 0 có -ký tự đầu tiên. Điều này có thể vô tình xảy ra trong các ngôn ngữ lập trình như C, trong đó rất dễ vô tình nhầm execvelẫn do quên chuyển tên chương trình được gọi như một phần của mảng đối số và các hàm tương tự, và nếu mọi người thường quên bảo vệ chống lại nó, nó cũng có thể kết thúc là bước cuối cùng trong một lỗ hổng có thể khai thác độc hại cho phép kẻ tấn công có được một trình bao tương tác.
mtraceur

161

Dòng shebang chưa bao giờ được chỉ định là một phần của POSIX, SUS, LSB hoặc bất kỳ thông số kỹ thuật nào khác. AFAIK, nó thậm chí còn chưa được ghi chép chính xác.

Có một sự đồng thuận thô sơ về những gì nó làm: lấy mọi thứ giữa cái !và cái \nexecnó. Giả định là mọi thứ giữa dấu !\nlà một đường dẫn tuyệt đối đầy đủ đến trình thông dịch. Không có sự đồng thuận về điều gì sẽ xảy ra nếu nó chứa khoảng trắng.

  1. Một số hệ điều hành chỉ đơn giản coi toàn bộ thứ là đường dẫn. Rốt cuộc, trong hầu hết các hệ điều hành, khoảng trắng hoặc dấu gạch ngang là hợp pháp trong một đường dẫn.
  2. Một số hệ điều hành phân tách ở khoảng trắng và coi phần đầu tiên là đường dẫn đến trình thông dịch và phần còn lại là các đối số riêng lẻ.
  3. Một số hệ điều hành phân tách ở khoảng trắng đầu tiên và coi phần phía trước là đường dẫn đến interpeter và phần còn lại như một đối số duy nhất (đó là những gì bạn đang thấy).
  4. Một số thậm chí không hỗ trợ dòng shebang nào cả .

Rất may, 1. và 4. dường như đã hết, nhưng 3. khá phổ biến, vì vậy bạn không thể dựa vào việc có thể vượt qua nhiều hơn một đối số.

Và vì vị trí của các lệnh cũng không được chỉ định trong POSIX hoặc SUS, bạn thường sử dụng hết đối số đó bằng cách chuyển tên của tệp thực thi đến envđể có thể xác định vị trí của tệp thực thi; ví dụ:

#!/usr/bin/env gawk

[Rõ ràng, điều này vẫn giả định một con đường cụ thể cho env, nhưng chỉ có rất ít hệ thống nơi nó tồn tại /bin, vì vậy điều này nói chung là an toàn. Vị trí của envđược chuẩn hóa hơn rất nhiều so với vị trí của gawkhoặc thậm chí tệ hơn một cái gì đó như pythonhoặc rubyhoặc spidermonkey.]

Đó có nghĩa là bạn không thể thực sự sử dụng bất kỳ đối số nào cả .


1
Env của FreeBSD có một -Scông tắc giúp ích ở đây, nhưng nó không có trên Linux của tôi envvà tôi nghi ngờ là không có trên gygwin. @hstoerr, những người dùng khác với các tình huống khác nhau có thể đọc câu hỏi của bạn sau đó, vì vậy, nói chung các câu trả lời di động sẽ được ưu tiên hơn, ngay cả khi bạn hiện không yêu cầu tính di động.
dubiousjim

4
Vì vậy, chúng ta không thể sử dụng các đối số một cách linh hoạt trong một nhóm. Nhưng nếu chúng ta cần lập luận bằng bất kỳ phương tiện nào cần thiết? Tôi đoán rằng giải pháp là viết một script shell của wrapper có chứa #!/bin/sh/usr/bin/env gawk --re-interval -f my-script.awk. Đúng không?
Rory O'Kane

1
Tôi không đồng ý. Bạn hoàn toàn có thể sử dụng một đối số. Bất kỳ hệ thống nào mà bạn không thể sử dụng bất kỳ đối số nào đều thất bại thảm hại trong việc triển khai Chủ nghĩa Unix truyền thống này, chính là hash-bang. Nếu việc không triển khai là trò chơi công bằng, thì chúng ta có thể nói một cách an toàn rằng #!bản thân nó không phải là di động. Ví dụ, Windows hoàn toàn không nhận ra quy ước này. Theo truyền thống, một đối số có tiếng nổ là cần thiết trên Unix để có thể thực hiện được #!/usr/bin/awk -f.
Kaz

7
@Kaz: Có, nhưng vì đường dẫn của nhiều mã nhị phân không được tiêu chuẩn hóa, nên bạn sử dụng hết một đối số của mình cho #!/usr/bin/env rubyhoặc thích.
Jörg W Mittag

3
@Pacerier: Thay đổi thông số kỹ thuật của POSIX và đợi 20-30 năm cho đến khi tất cả các hệ thống được cập nhật để tuân thủ thông số kỹ thuật.
Jörg W Mittag

18

Mặc dù không phải là di động chính xác, nhưng bắt đầu với coreutils 8.30 và theo tài liệu của nó, bạn sẽ có thể sử dụng:

#!/usr/bin/env -S command arg1 arg2 ...

Vì vậy, đã cho:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

bạn sẽ nhận được:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

và trong trường hợp bạn tò mò showargslà:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

Câu trả lời gốc ở đây .


1
FYI, FreeBSD đã có -S trong nhiều năm (kể từ 6.0). Đây là một bổ sung khả năng di động đáng hoan nghênh cho các lõi.
Juan

12

Tôi đã gặp phải vấn đề tương tự, không có giải pháp rõ ràng vì cách các khoảng trắng được xử lý trong một nhóm (ít nhất là trên Linux).

Tuy nhiên, bạn có thể chuyển một số tùy chọn trong một nhóm, miễn là chúng là các tùy chọn ngắn và chúng có thể được nối với nhau (theo cách GNU).

Ví dụ, bạn không thể có

#!/usr/bin/foo -i -f

nhưng bạn có thể có

#!/usr/bin/foo -if

Rõ ràng, điều đó chỉ hoạt động khi các tùy chọn có giá trị tương đương ngắn và không có đối số.


11

Dưới Cygwin và Linux, mọi thứ sau khi đường dẫn của shebang được phân tích thành chương trình như một đối số.

Có thể hack xung quanh điều này bằng cách sử dụng một awktập lệnh khác bên trong shebang:

#!/usr/bin/gawk {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}

Điều này sẽ thực thi {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}trong awk.
Và điều này sẽ thực thi /usr/bin/gawk --re-interval -f path/to/your/script.awktrong trình bao hệ thống của bạn.


2
wont work này nếu youve truyền đối số cho kịch bản
Steven Penny

4
#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

Thủ thuật shebang shell trên là dễ di động hơn /usr/bin/env.


'' ':' Là lệnh giữ lại vì giải pháp ban đầu của tôi là dành cho tập lệnh python nên '' ':' yêu cầu trình thông dịch python bỏ qua phần thực thi.
user3123730

4
Tôi nghĩ rằng bạn đang bị phản đối vì giải pháp của bạn là dành cho python, nhưng câu hỏi này là về awk.
Aaron McDaid,

1
Hack tuyệt vời cho python.
Zaar Hai,

3

Trong hướng dẫn sử dụng gawk (http://www.gnu.org/manual/gawk/gawk.html), phần cuối của phần 1.14 lưu ý rằng bạn chỉ nên sử dụng một đối số khi chạy gawk từ một dòng shebang. Nó nói rằng hệ điều hành sẽ coi mọi thứ sau đường dẫn tới gawk như một đối số duy nhất. Có lẽ có một cách khác để chỉ định --re-intervaltùy chọn? Có lẽ tập lệnh của bạn có thể tham chiếu trình bao của bạn trong dòng shebang, chạy gawkdưới dạng lệnh và bao gồm văn bản của tập lệnh của bạn dưới dạng "tài liệu tại đây".


Có vẻ như không có cách nào khác để chỉ định tùy chọn. Bạn nói đúng: gawk -f - << EOF, một số dòng script, EOF hoạt động, nhưng nó ngăn tôi đọc đầu vào chuẩn bằng gawk.
Hans-Peter Störr

Tài liệu ở đây sử dụng luồng đầu vào tiêu chuẩn cho gawk, nhưng bạn vẫn có thể đưa một thứ gì đó vào over stderr (nghĩa là chuyển hướng stdout sang stderr trước khi chuyển hướng vào tập lệnh này). Tôi chưa bao giờ thực sự thử điều đó nhưng miễn là quy trình đầu tiên không phát ra bất kỳ thứ gì trên stderr, nó có thể hoạt động. Bạn cũng có thể tạo một đường ống có tên ( linuxjournal.com/content/using-name-pipes-fifos-bash ) nếu bạn muốn đảm bảo rằng không có gì khác đang sử dụng nó.
bta

3

Tại sao không sử dụng bashgawkchính nó, để bỏ qua shebang, đọc script và chuyển nó dưới dạng tệp cho phiên bản thứ hai của gawk [--with-whatever-number-of-params-you-need]?

#!/bin/bash
gawk --re-interval -f <(gawk 'NR>3' $0 )
exit
{
  print "Program body goes here"
  print $1
}

(- điều tương tự cũng có thể được thực hiện một cách tự nhiên với ví dụ sedhoặc tail, nhưng tôi nghĩ rằng có một số loại vẻ đẹp chỉ phụ thuộc vào bashgawkchính nó;)


0

Chỉ cho vui thôi: có một giải pháp khá kỳ lạ sau đây định tuyến lại stdin và chương trình thông qua các bộ mô tả tệp 3 và 4. Bạn cũng có thể tạo một tệp tạm thời cho tập lệnh.

#!/bin/bash
exec 3>&0
exec <<-EOF 4>&0
BEGIN {print "HALLO"}
{print \$1}
EOF
gawk --re-interval -f <(cat 0>&4) 0>&3

Có một điều khó chịu về điều này: shell thực hiện mở rộng biến trên script, vì vậy bạn phải trích dẫn mỗi $ (như được thực hiện trong dòng thứ hai của script) và có thể hơn thế nữa.


-1

Đối với một giải pháp di động, hãy sử dụng awkthay vì gawkgọi shell BOURNE tiêu chuẩn ( /bin/sh) bằng shebang của bạn và gọi awktrực tiếp, truyền chương trình trên dòng lệnh dưới dạng tài liệu tại đây thay vì thông qua stdin:

#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF

Lưu ý: không -fđối số awk. Điều đó stdincó sẵn awkđể đọc đầu vào từ. Giả sử bạn đã gawkcài đặt và trên của bạn PATH, điều đó đạt được mọi thứ mà tôi nghĩ rằng bạn đang cố gắng làm với ví dụ ban đầu của mình (giả sử bạn muốn nội dung tệp là tập lệnh awk chứ không phải đầu vào, mà tôi nghĩ cách tiếp cận shebang của bạn sẽ coi nó là ).


3
Điều đó không hiệu quả với tôi. Người đàn ông bash nói <<< blabla đặt blabla trên stdin. Ý bạn là << - EOF? Dù bằng cách nào, điều đó cũng đặt chương trình trên stdin.
Hans-Peter Störr
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.