Bagaimana cara menggunakan beberapa argumen untuk awk dengan shebang (mis. #!)?

118

Saya ingin menjalankan skrip gawk dengan --re-intervalmenggunakan shebang. Pendekatan "naif" dari

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

tidak berfungsi, karena gawk dipanggil dengan argumen pertama "--re-interval -f"(tidak dipisahkan di sekitar spasi), yang tidak dipahami. Apakah ada solusi untuk itu?

Tentu saja Anda tidak dapat memanggil gawk secara langsung tetapi membungkusnya ke dalam skrip shell yang memisahkan argumen pertama, atau membuat skrip shell yang kemudian memanggil gawk dan memasukkan skrip ke file lain, tetapi saya bertanya-tanya apakah ada cara untuk melakukannya ini dalam satu file.

Perilaku baris shebang berbeda dari satu sistem ke sistem lainnya - setidaknya di Cygwin tidak membagi argumen dengan spasi. Saya hanya peduli tentang bagaimana melakukannya pada sistem yang berperilaku seperti itu; skrip tidak dimaksudkan untuk dibawa-bawa.

Hans-Peter Störr
sumber
1
Eksperimen konyol yang baru saja saya lakukan adalah dengan satu skrip menggunakan skrip lain pada baris shebang, yang membagi argumen dengan benar.
Hasturkun
@Hasturkun, yang menimbulkan masalah lain, bahwa perilaku baris shebang juga berbeda dari satu sistem ke sistem lainnya apakah program yang dipanggil itu sendiri dapat berupa skrip.
dubiousjim
Dengan versi terbaru dari gawk (> = 4.0), --re-intervaltidak diperlukan lagi (lihat [ gnu.org/software/gawk/manual/… ).

Jawaban:

25

Ini sepertinya berhasil untuk saya dengan (g) awk.

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


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

Perhatikan #!prosesnya /bin/sh, jadi skrip ini pertama kali diinterpretasikan sebagai skrip shell.

Pada awalnya, saya hanya mencoba "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@", tetapi canggung memperlakukannya sebagai perintah dan mencetak setiap baris input tanpa syarat. Itulah mengapa saya memasukkan arbitrary_long_name==0- itu seharusnya gagal sepanjang waktu. Anda bisa menggantinya dengan beberapa string omong kosong. Pada dasarnya, saya mencari kondisi palsu di awk yang tidak akan mempengaruhi skrip shell.

Dalam skrip shell, arbitrary_long_name==0definisikan variabel yang dipanggil arbitrary_long_namedan setel sama dengan =0.

Aaron McDaid
sumber
Ini adalah jawaban saya, tetapi saya ingin tahu apakah ini cukup portabel dan kuat. Apakah ini bergantung secara khusus pada bash, atau akankah berfungsi dengan POSIX apa pun sh? Dan saya tidak awksering menggunakannya , jadi saya tidak yakin trik saya di baris kedua adalah cara yang baik untuk memaksa awkmengabaikan baris.
Aaron McDaid
Hanya apa yang saya ingin tahu, +1, tetapi mungkin tidak disarankan (karena itu suara relatif).
Aaron Hall
Bisakah Anda menjelaskan masalah apa yang mungkin terjadi, @AaronHall? Selama variabel arbitrary_long_nametidak berbenturan dengan variabel yang digunakan dalam program awk yang sebenarnya, saya tidak dapat melihat masalah apa pun. Apakah ada sesuatu yang saya lewatkan?
Aaron McDaid
Gunakan #!/bin/sh -alih-alih #!/bin/shuntuk melindungi skrip dari kemungkinan berperilaku buruk dengan cara yang berbahaya jika dipanggil dengan argumen ke nol yang dimiliki -sebagai karakter pertama. Hal ini dapat terjadi secara tidak sengaja dalam bahasa pemrograman seperti C, di mana mudah untuk secara tidak sengaja mengacaukan dengan lupa meneruskan nama program yang dipanggil sebagai bagian dari array argumen ke execvedan fungsi serupa, dan jika orang biasanya lupa untuk melindunginya, itu juga bisa akhirnya menjadi langkah terakhir dalam kerentanan yang dapat dieksploitasi secara jahat yang memungkinkan penyerang mendapatkan shell interaktif.
mtraceur
161

Garis shebang tidak pernah ditentukan sebagai bagian dari POSIX, SUS, LSB atau spesifikasi lainnya. AFAIK, bahkan belum terdokumentasi dengan baik.

Ada konsensus kasar tentang apa yang dilakukannya: mengambil segala sesuatu antara the !dan the \ndan execitu. Asumsinya adalah bahwa segala sesuatu antara the !dan the \nadalah jalur absolut penuh ke penafsir. Tidak ada konsensus tentang apa yang terjadi jika itu berisi spasi.

  1. Beberapa sistem operasi hanya memperlakukan semuanya sebagai jalur. Lagi pula, di sebagian besar sistem operasi, spasi atau tanda hubung legal dalam satu jalur.
  2. Beberapa sistem operasi terpisah pada spasi kosong dan memperlakukan bagian pertama sebagai jalur ke penerjemah dan sisanya sebagai argumen individual.
  3. Beberapa sistem operasi terpecah pada whitespace pertama dan memperlakukan bagian depan sebagai jalur ke interpeter dan sisanya sebagai argumen tunggal (yang Anda lihat).
  4. Beberapa bahkan tidak mendukung garis shebang sama sekali .

Untungnya, 1. dan 4. tampaknya telah mati, tetapi 3. cukup tersebar luas, jadi Anda tidak bisa mengandalkan kemampuan untuk menyampaikan lebih dari satu argumen.

Dan karena lokasi perintah juga tidak ditentukan dalam POSIX atau SUS, Anda umumnya menggunakan up yang argumen tunggal dengan melewati executable nama untuk envsehingga hal itu dapat menentukan lokasi executable; misalnya:

#!/usr/bin/env gawk

[Jelas, ini masih mengasumsikan jalur tertentu untuk env, tetapi hanya ada sedikit sistem di mana ia hidup /bin, jadi ini umumnya aman. Lokasi envjauh lebih terstandarisasi daripada lokasi gawkatau bahkan lebih buruk seperti pythonatau rubyatau spidermonkey.]

Artinya, Anda tidak dapat benar-benar menggunakan argumen apa pun sama sekali .

Jörg W Mittag
sumber
1
Env FreeBSD memiliki -Ssakelar yang membantu di sini, tetapi tidak ada di Linux saya env, dan saya curiga juga tidak tersedia di gygwin. @hstoerr, pengguna lain dengan situasi berbeda mungkin membaca pertanyaan Anda nanti, jadi secara umum jawaban portabel lebih disukai, bahkan jika Anda sekarang tidak memerlukan portabilitas.
dubiousjim
4
Jadi kita tidak bisa menggunakan argumen secara portabel dalam shebang. Tetapi bagaimana jika kita membutuhkan argumen dengan cara apa pun yang diperlukan? Saya menduga bahwa solusinya adalah menulis skrip shell pembungkus yang berisi #!/bin/shdan /usr/bin/env gawk --re-interval -f my-script.awk. Apakah itu benar?
Rory O'Kane
1
Saya tidak setuju. Anda bisa menggunakan satu argumen dengan mudah. Sistem apa pun di mana Anda tidak dapat menggunakan argumen apa pun gagal total untuk menerapkan Unixisme tradisional ini, yang merupakan hash-bang. Jika non-implementasi adalah permainan yang adil, maka kita dapat dengan aman mengatakan bahwa #!itu sendiri tidak portabel. Misalnya, Windows sama sekali tidak mengenali konvensi ini "secara asli". Sebuah argumen yang kuat diperlukan pada Unix secara tradisional untuk dapat melakukannya #!/usr/bin/awk -f.
Kaz
7
@Kaz: Ya, tetapi karena path dari banyak binari tidak distandarisasi, Anda menggunakan satu argumen Anda untuk #!/usr/bin/env rubyatau like.
Jörg W Mittag
3
@Pacerier: Ubah spesifikasi POSIX dan tunggu 20-30 tahun hingga semua sistem diperbarui agar sesuai dengan spesifikasi.
Jörg W Mittag
18

Meskipun tidak terlalu portabel, dimulai dengan coreutils 8.30 dan menurut dokumentasinya, Anda akan dapat menggunakan:

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

Jadi diberikan:

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

kamu akan mendapatkan:

% ./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'

dan jika Anda penasaran showargsadalah:

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

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

Jawaban asli di sini .

unode
sumber
1
FYI, FreeBSD telah memiliki -S selama bertahun-tahun (sejak 6.0). Ini adalah tambahan portabilitas yang disambut baik untuk coreutils.
Juan
12

Saya menemukan masalah yang sama, tanpa solusi yang jelas karena cara spasi putih ditangani dalam shebang (setidaknya di Linux).

Namun, Anda dapat memberikan beberapa opsi dalam shebang, selama opsi tersebut pendek dan dapat digabungkan (dengan cara GNU).

Misalnya, Anda tidak dapat memiliki

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

tapi kamu bisa

#!/usr/bin/foo -if

Jelas, itu hanya berfungsi ketika opsinya memiliki padanan pendek dan tidak mengambil argumen.

ℝaphink
sumber
11

Di bawah Cygwin dan Linux semuanya setelah jalur shebang diurai ke program sebagai satu argumen.

Dimungkinkan untuk meretas ini dengan menggunakan awkskrip lain di dalam shebang:

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

Ini akan dieksekusi {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}dalam awk.
Dan ini akan dijalankan /usr/bin/gawk --re-interval -f path/to/your/script.awkdi shell sistem Anda.

Moritz
sumber
2
ini tidak akan berhasil jika Anda telah memberikan argumen ke naskah
Steven Penny
4
#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

Trik shell shebang di atas lebih portabel daripada /usr/bin/env.

pengguna3123730
sumber
'' ':' Adalah penahan karena solusi asli saya adalah untuk skrip python sehingga '' ':' memberi tahu juru bahasa python untuk mengabaikan bagian exec.
pengguna3123730
4
Saya pikir Anda tidak disukai karena solusi Anda adalah untuk python, tetapi pertanyaan ini adalah tentang awk.
Aaron McDaid
1
Retasan hebat untuk python.
Zaar Hai
3

Dalam manual gawk (http://www.gnu.org/manual/gawk/gawk.html), di akhir bagian 1.14 perhatikan bahwa Anda hanya boleh menggunakan satu argumen saat menjalankan gawk dari baris shebang. Dikatakan bahwa OS akan memperlakukan semuanya setelah jalan melongo sebagai argumen tunggal. Mungkin ada cara lain untuk menentukan file--re-interval opsi? Mungkin skrip Anda dapat mereferensikan shell Anda di baris shebang, dijalankan gawksebagai perintah, dan menyertakan teks skrip Anda sebagai "dokumen di sini".

bta
sumber
Sepertinya tidak ada cara lain untuk menentukan opsi. Anda benar: gawk -f - << EOF, beberapa baris skrip, EOF berfungsi, tetapi itu menghalangi saya dari membaca input standar dengan gawk.
Hans-Peter Störr
Dokumen di sini memakan aliran input standar untuk gawk, tetapi Anda mungkin masih dapat menyalurkan sesuatu di lebih dari stderr (yaitu, alihkan stdout ke stderr sebelum menyalurkan ke skrip ini). Saya belum pernah benar-benar mencobanya tetapi selama proses pertama tidak mengeluarkan apa pun di stderr, itu mungkin berhasil. Anda juga dapat membuat pipa bernama ( linuxjournal.com/content/using-named-pipes-fifos-bash ) jika Anda ingin memastikan tidak ada orang lain yang menggunakannya.
bta
3

Mengapa tidak menggunakan bashdan gawkdirinya sendiri, untuk melewati shebang, membaca skrip, dan meneruskannya sebagai file ke contoh kedua 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
}

(-yang sama secara alami juga dapat dicapai dengan misalnya sedatau tail, tetapi saya pikir ada semacam keindahan yang hanya bergantung pada bashdan gawkdirinya sendiri;)

conny
sumber
0

Hanya untuk kesenangan: ada solusi yang cukup aneh berikut yang mengubah rute stdin dan program melalui deskriptor file 3 dan 4. Anda juga dapat membuat file sementara untuk skrip.

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

Satu hal yang mengganggu tentang ini: shell melakukan ekspansi variabel pada skrip, jadi Anda harus mengutip setiap $ (seperti yang dilakukan di baris kedua skrip) dan mungkin lebih dari itu.

Hans-Peter Störr
sumber
-1

Untuk solusi portabel, gunakan awkdaripada gawk, panggil BOURNE shell ( /bin/sh) standar dengan shebang Anda, dan panggil awksecara langsung, meneruskan program pada baris perintah sebagai dokumen di sini daripada melalui stdin:

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

Catatan: tidak ada -fargumen untuk awk. Yang meninggalkan stdintersedia untuk awkmembaca masukan dari. Dengan asumsi Anda telah gawkmenginstal dan pada Anda PATH, itu mencapai semua yang saya pikir Anda coba lakukan dengan contoh asli Anda (dengan asumsi Anda ingin konten file menjadi skrip awk dan bukan input, yang menurut saya pendekatan shebang Anda akan memperlakukannya sebagai ).

lharper71
sumber
3
Itu tidak berhasil untuk saya. Pria bash berkata <<< blabla menempatkan blabla di stdin. Apakah yang Anda maksud << - EOF? Bagaimanapun, itu juga menempatkan program di stdin.
Hans-Peter Störr