Seperti kata Chris , argumen formulir variablename=anythingdiperlakukan sebagai penugasan variabel (yang dilakukan pada saat argumen diproses sebagai lawan dari yang (lebih baru) -v var=valueyang dilakukan sebelum BEGINpernyataan) alih-alih memasukkan nama file input.
Tapi itu menghalangi ketika Anda memiliki file yang namanya berisi =karakter.
Sekarang, itu hanya masalah ketika apa yang tersisa dari yang pertama =adalah awknama variabel yang valid .
Apa yang merupakan nama variabel yang valid di awklebih ketat daripada di sh.
POSIX membutuhkannya seperti:
[_a-zA-Z][_a-zA-Z0-9]*
Dengan hanya karakter set karakter portabel. Namun, /usr/xpg4/bin/awkSolaris 11 setidaknya tidak sesuai dalam hal itu dan memungkinkan setiap karakter alfabet di lokal dalam nama variabel, bukan hanya a-zA-Z.
Jadi argumen seperti x+y=fooatau =baratau ./foo=barmasih diperlakukan sebagai nama file input dan bukan tugas sebagai apa yang tersisa dari yang pertama =bukan nama variabel yang valid. Argumen suka Stéphane=Chazelas.txtmungkin atau tidak mungkin, tergantung pada awkimplementasi dan lokal.
Itu sebabnya dengan awk, disarankan untuk menggunakan:
awk '...'./*.txt
dari pada
awk '...'*.txt
misalnya untuk menghindari masalah jika Anda tidak dapat menjamin nama txtfile tidak akan mengandung =karakter.
Juga, berhati-hatilah karena argumen seperti -vfoo=bar.txtdapat dianggap sebagai opsi jika Anda menggunakan:
awk -f file.awk -vfoo=bar.txt
(juga berlaku untuk awk '{code}' -vfoo=bar.txtdengan awkdari versi busybox sebelum 1.28.0, lihat laporan bug yang sesuai ).
Sekali lagi, menggunakan ./*.txtkarya di sekitar itu (menggunakan ./awalan juga membantu dengan file yang disebut -yang sebaliknya awkdipahami sebagai input standar berarti ).
Itu juga sebabnya
#! /usr/bin/awk -f
Shebang tidak bekerja. Sementara var=valueyang bisa dikerjakan dengan memperbaikiARGV nilai - nilai (tambahkan ./awalan) dalam sebuah BEGINpernyataan:
#! /usr/bin/awk -f
BEGIN {for(i =1; i < ARGC; i++)if(ARGV[i]~/^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i]="./" ARGV[i]}# rest of awk script
Itu tidak akan membantu dengan yang opsi karena yang dilihat oleh awkdan bukan awkskrip.
Salah satu masalah kosmetik potensial dengan menggunakan ./awalan itu adalah akhirnya FILENAME, tetapi Anda selalu dapat menggunakannya substr(FILENAME, 3)untuk menghapusnya jika Anda tidak menginginkannya.
Implementasi GNU awkmemperbaiki semua masalah tersebut dengan -Eopsinya.
Setelah -E, gawk hanya mengharapkan path dari awkskrip (di mana -masih berarti stdin) dan kemudian daftar path file input saja (dan di sana, bahkan tidak -diperlakukan secara khusus).
Ini dirancang khusus untuk:
#! /usr/bin/gawk -E
shebang di mana daftar argumen selalu memasukkan file (perhatikan bahwa Anda masih dapat mengedit ARGVdaftar itu dalam sebuah BEGINpernyataan).
Anda juga dapat menggunakannya sebagai:
gawk -e '...awk code here...'-E /dev/null *.txt
Kami menggunakan -Edengan skrip kosong ( /dev/null) hanya untuk memastikan bahwa *.txtsetelah itu selalu diperlakukan sebagai file input, meskipun mengandung =karakter.
Saya tidak melihat bagaimana jalur eksplisit yang berakhir di FILENAME merupakan masalah. Entah skrip awk bersifat umum, dalam hal ini skrip harus menangani semua jenis jalur yang berakhir di FILENAME (termasuk tetapi tidak terbatas pada ../foo, /path/to/foodan jalur yang ada dalam penyandian berbeda) - dalam hal substr(FILENAME,3)ini tidak akan cukup, atau itu skrip satu tembakan di mana pengguna pada dasarnya tahu apa nama file - dalam hal ini / dia mungkin tidak perlu repot-repot dengan salah satu dari mereka yang mengandung =salah satu ;-)
mosvy
2
@ Mosvy Saya tidak berpikir itu menyatakan begitu banyak yang ./merupakan masalah, tetapi itu mungkin tidak diinginkan dalam kondisi tertentu, seperti kasus di mana nama file harus dimasukkan dalam output, dalam hal ini ./harus berlebihan dan tidak perlu, sehingga Anda Akan perlu untuk menyingkirkannya entah bagaimana. Setidaknya ada satu contoh . Adapun pengguna mengetahui apa nama file - baik, dalam hal ini kita juga tahu apa nama file, tetapi =masih menghalangi proses pengolahan yang tepat. Jadi bisa memimpin -menghalangi.
Sergiy Kolodyazhnyy
@ Mosvy, ya idenya adalah bahwa Anda ingin menggunakan ./awalan untuk mengatasi awkfitur (mis) itu, tetapi kemudian Anda berakhir dengan yang ./pada keluaran yang mungkin ingin Anda hapus. Lihat cara memeriksa apakah baris pertama file berisi string tertentu? sebagai contoh.
Stéphane Chazelas
Bukan hanya lokal (relatif terhadap direktori ini) ./tetapi juga global (jalur absolut) /yang membuat awk menafsirkan argumen sebagai file.
Isaac
21
Dalam sebagian besar versi awk, argumen setelah program untuk dieksekusi adalah:
File
Penugasan formulir x=y
Karena nama file Anda ditafsirkan sebagai kasus # 2, awk masih menunggu sesuatu untuk dibaca di stdin (karena tidak merasa bahwa ada nama file yang dilewati).
Salah satu dari dua jenis argumen berikut ini dapat dicampur:
file: Nama path dari file yang berisi input untuk dibaca, yang cocok dengan set pola dalam program. Jika tidak ada operan file yang ditentukan, atau jika operan file adalah '-', input standar harus digunakan.
tugas: Suatu operan yang dimulai dengan karakter garis bawah atau alfabet dari set karakter portabel (lihat tabel dalam volume Definisi Basis IEEE Std 1003.1-2001, Bagian 6.1, Set Karakter Portabel), diikuti oleh urutan garis bawah, angka, dan alfabet dari set karakter portabel, diikuti oleh karakter '=', harus menentukan tugas variabel daripada nama path.
Dengan demikian, mudah dibawa, Anda memiliki beberapa opsi (# 1 kemungkinan adalah yang paling tidak mengganggu):
Gunakan awk ... ./my=file, yang menghindari ini karena .bukan "karakter garis bawah atau alfabet dari set karakter portabel".
Letakkan file di stdin menggunakan awk ... < my=file. Namun, ini tidak berfungsi dengan baik pada banyak file.
Buat hardlink ke file sementara, dan gunakan itu. Anda dapat melakukan sesuatu seperti ln my=file my_file, dan kemudian gunakan my_fileseperti biasa. Tidak ada penyalinan yang akan dilakukan, dan kedua file akan didukung oleh data dan metadata inode yang sama. Setelah menggunakannya, aman untuk menghapus tautan yang dibuat karena jumlah referensi ke inode akan tetap lebih besar dari 0.
Tidak ./my=file bekerja % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory). Ini harus portabel karena ./mybukan nama variabel yang valid, jadi tidak boleh diuraikan seperti itu.
Stephen Harris
2
Seperti yang dikatakan teks POSIX, masalahnya hanya ketika yang pertama =didahului oleh karakter garis bawah atau alfabet dari set karakter portabel (lihat tabel dalam volume Definisi Dasar IEEE Std 1003.1-2001, Bagian 6.1, Set Karakter Portable), diikuti oleh urutan garis bawah, angka, dan alfabet dari set karakter portabel . jadi path file seperti ++foo=bar.txtatau =fooatau ./foo=barsemuanya OK karena itu .atau +bukan [_a-zA-Z].
Stéphane Chazelas
1
@SergiyKolodyazhnyy awk adalah eksternal ke shell, jadi tidak masalah yang Anda gunakan. ./my=fileakan melewati kata demi kata.
Chris Down
1
@SergiyKolodyazhnyy, sama untuk awk '{print $1,$2}' /etc/passwd. Intinya adalah bahwa memiliki shell membuka file sebagai lawan awk tidak membuat perbedaan, apakah itu membuatnya dapat dicari atau tidak. Sebenarnya, dalam awk '{exit}' < /etc/passwd, Anda akan berharap awkuntuk mencari kembali ke akhir catatan pertama setelah itu exituntuk memastikan itu meninggalkan posisi dalam stdin di sana. POSIX mensyaratkan itu. /usr/xpg4/bin/awkmelakukannya di Solaris, tetapi tampaknya tidak gawkjuga mawkmelakukannya di GNU / Linux.
Stéphane Chazelas
3
@mosvy, lihat bagian INPUT FILES di pubs.opengroup.org/onlinepubs/9699919799/utilities/... Berguna dalam sejumlah pola penggunaan yang hanya masuk akal dengan file biasa seperti ketika Anda ingin memotong file atau menulis data ke dalamnya di posisi yang diidentifikasi dengan awkcara itu.
Setiap argumen tambahan pada baris perintah biasanya diperlakukan sebagai file input untuk diproses dalam urutan yang ditentukan. Namun, argumen yang memiliki bentuk var = nilai, memberikan nilai nilai ke variabel var - itu tidak menentukan file sama sekali.
Mengapa perintah itu berhenti dan menunggu? Karena dalam bentuk awk 'processing_script_here' my=file.txttidak ada file yang ditentukan oleh definisi di atas - my=file.txtditafsirkan sebagai penugasan variabel, dan jika tidak ada file yang didefinisikan awkakan membaca stdin (juga jelas dari straceyang menunjukkan bahwa awk dalam perintah tersebut sedang menungguread(0,'...) syscall.
Ini juga didokumentasikan dalam spesifikasi POSIX awk , lihat bagian dan tugas operan bagian dari itu)
Tugas variabel jelas dalam awk '{print foo}' foo=bar /etc/passwdnilai foodicetak untuk setiap baris di / etc / passwd. Menentukan./foo=bar atau path lengkap tidak berfungsi.
Perhatikan bahwa berjalan stracepada awk '1' foo=barserta memeriksa dengancat foo=bar menunjukkan bahwa ini adalah masalah awk spesifik, dan execve melakukan acara nama file sebagai argumen berlalu, sehingga kerang tidak ada hubungannya dengan tugas variabel env dalam kasus ini.
Selain itu, harap dicatat bahwa awk '...script...' foo=bartidak akan menyebabkan pembuatan variabel lingkungan oleh shell, karena tugas variabel lingkungan harus mendahului perintah untuk berlaku. Lihat POSIX Shell Grammar Rules , poin nomor 7. Selain itu, ini dapat diverifikasi melaluiawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd
Jawaban:
Seperti kata Chris , argumen formulir
variablename=anything
diperlakukan sebagai penugasan variabel (yang dilakukan pada saat argumen diproses sebagai lawan dari yang (lebih baru)-v var=value
yang dilakukan sebelumBEGIN
pernyataan) alih-alih memasukkan nama file input.Itu bisa berguna dalam hal-hal seperti:
Di mana Anda dapat menentukan file yang berbeda
FS
/RS
per. Ini juga biasa digunakan di:Yang merupakan versi lebih aman dari:
(yang tidak berfungsi jika
file1
kosong)Tapi itu menghalangi ketika Anda memiliki file yang namanya berisi
=
karakter.Sekarang, itu hanya masalah ketika apa yang tersisa dari yang pertama
=
adalahawk
nama variabel yang valid .Apa yang merupakan nama variabel yang valid di
awk
lebih ketat daripada dish
.POSIX membutuhkannya seperti:
Dengan hanya karakter set karakter portabel. Namun,
/usr/xpg4/bin/awk
Solaris 11 setidaknya tidak sesuai dalam hal itu dan memungkinkan setiap karakter alfabet di lokal dalam nama variabel, bukan hanya a-zA-Z.Jadi argumen seperti
x+y=foo
atau=bar
atau./foo=bar
masih diperlakukan sebagai nama file input dan bukan tugas sebagai apa yang tersisa dari yang pertama=
bukan nama variabel yang valid. Argumen sukaStéphane=Chazelas.txt
mungkin atau tidak mungkin, tergantung padaawk
implementasi dan lokal.Itu sebabnya dengan awk, disarankan untuk menggunakan:
dari pada
misalnya untuk menghindari masalah jika Anda tidak dapat menjamin nama
txt
file tidak akan mengandung=
karakter.Juga, berhati-hatilah karena argumen seperti
-vfoo=bar.txt
dapat dianggap sebagai opsi jika Anda menggunakan:(juga berlaku untuk
awk '{code}' -vfoo=bar.txt
denganawk
dari versi busybox sebelum 1.28.0, lihat laporan bug yang sesuai ).Sekali lagi, menggunakan
./*.txt
karya di sekitar itu (menggunakan./
awalan juga membantu dengan file yang disebut-
yang sebaliknyaawk
dipahami sebagai input standar berarti ).Itu juga sebabnya
Shebang tidak bekerja. Sementara
var=value
yang bisa dikerjakan dengan memperbaikiARGV
nilai - nilai (tambahkan./
awalan) dalam sebuahBEGIN
pernyataan:Itu tidak akan membantu dengan yang opsi karena yang dilihat oleh
awk
dan bukanawk
skrip.Salah satu masalah kosmetik potensial dengan menggunakan
./
awalan itu adalah akhirnyaFILENAME
, tetapi Anda selalu dapat menggunakannyasubstr(FILENAME, 3)
untuk menghapusnya jika Anda tidak menginginkannya.Implementasi GNU
awk
memperbaiki semua masalah tersebut dengan-E
opsinya.Setelah
-E
, gawk hanya mengharapkan path dariawk
skrip (di mana-
masih berarti stdin) dan kemudian daftar path file input saja (dan di sana, bahkan tidak-
diperlakukan secara khusus).Ini dirancang khusus untuk:
shebang di mana daftar argumen selalu memasukkan file (perhatikan bahwa Anda masih dapat mengedit
ARGV
daftar itu dalam sebuahBEGIN
pernyataan).Anda juga dapat menggunakannya sebagai:
Kami menggunakan
-E
dengan skrip kosong (/dev/null
) hanya untuk memastikan bahwa*.txt
setelah itu selalu diperlakukan sebagai file input, meskipun mengandung=
karakter.sumber
../foo
,/path/to/foo
dan jalur yang ada dalam penyandian berbeda) - dalam halsubstr(FILENAME,3)
ini tidak akan cukup, atau itu skrip satu tembakan di mana pengguna pada dasarnya tahu apa nama file - dalam hal ini / dia mungkin tidak perlu repot-repot dengan salah satu dari mereka yang mengandung=
salah satu ;-)./
merupakan masalah, tetapi itu mungkin tidak diinginkan dalam kondisi tertentu, seperti kasus di mana nama file harus dimasukkan dalam output, dalam hal ini./
harus berlebihan dan tidak perlu, sehingga Anda Akan perlu untuk menyingkirkannya entah bagaimana. Setidaknya ada satu contoh . Adapun pengguna mengetahui apa nama file - baik, dalam hal ini kita juga tahu apa nama file, tetapi=
masih menghalangi proses pengolahan yang tepat. Jadi bisa memimpin-
menghalangi../
awalan untuk mengatasiawk
fitur (mis) itu, tetapi kemudian Anda berakhir dengan yang./
pada keluaran yang mungkin ingin Anda hapus. Lihat cara memeriksa apakah baris pertama file berisi string tertentu? sebagai contoh../
tetapi juga global (jalur absolut)/
yang membuat awk menafsirkan argumen sebagai file.Dalam sebagian besar versi awk, argumen setelah program untuk dieksekusi adalah:
x=y
Karena nama file Anda ditafsirkan sebagai kasus # 2, awk masih menunggu sesuatu untuk dibaca di stdin (karena tidak merasa bahwa ada nama file yang dilewati).
Mudahnya, perilaku ini didokumentasikan dalam POSIX :
Dengan demikian, mudah dibawa, Anda memiliki beberapa opsi (# 1 kemungkinan adalah yang paling tidak mengganggu):
awk ... ./my=file
, yang menghindari ini karena.
bukan "karakter garis bawah atau alfabet dari set karakter portabel".awk ... < my=file
. Namun, ini tidak berfungsi dengan baik pada banyak file.ln my=file my_file
, dan kemudian gunakanmy_file
seperti biasa. Tidak ada penyalinan yang akan dilakukan, dan kedua file akan didukung oleh data dan metadata inode yang sama. Setelah menggunakannya, aman untuk menghapus tautan yang dibuat karena jumlah referensi ke inode akan tetap lebih besar dari 0.sumber
./my=file
bekerja% awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
Ini harus portabel karena./my
bukan nama variabel yang valid, jadi tidak boleh diuraikan seperti itu.=
didahului oleh karakter garis bawah atau alfabet dari set karakter portabel (lihat tabel dalam volume Definisi Dasar IEEE Std 1003.1-2001, Bagian 6.1, Set Karakter Portable), diikuti oleh urutan garis bawah, angka, dan alfabet dari set karakter portabel . jadi path file seperti++foo=bar.txt
atau=foo
atau./foo=bar
semuanya OK karena itu.
atau+
bukan[_a-zA-Z]
../my=file
akan melewati kata demi kata.awk '{print $1,$2}' /etc/passwd
. Intinya adalah bahwa memiliki shell membuka file sebagai lawan awk tidak membuat perbedaan, apakah itu membuatnya dapat dicari atau tidak. Sebenarnya, dalamawk '{exit}' < /etc/passwd
, Anda akan berharapawk
untuk mencari kembali ke akhir catatan pertama setelah ituexit
untuk memastikan itu meninggalkan posisi dalam stdin di sana. POSIX mensyaratkan itu./usr/xpg4/bin/awk
melakukannya di Solaris, tetapi tampaknya tidakgawk
jugamawk
melakukannya di GNU / Linux.awk
cara itu.Mengutip dokumentasi gawk (penekanan catatan ditambahkan):
Mengapa perintah itu berhenti dan menunggu? Karena dalam bentuk
awk 'processing_script_here' my=file.txt
tidak ada file yang ditentukan oleh definisi di atas -my=file.txt
ditafsirkan sebagai penugasan variabel, dan jika tidak ada file yang didefinisikanawk
akan membaca stdin (juga jelas daristrace
yang menunjukkan bahwa awk dalam perintah tersebut sedang menungguread(0,'...)
syscall.Ini juga didokumentasikan dalam spesifikasi POSIX awk , lihat bagian dan tugas operan bagian dari itu)
Tugas variabel jelas dalam
awk '{print foo}' foo=bar /etc/passwd
nilaifoo
dicetak untuk setiap baris di / etc / passwd. Menentukan./foo=bar
atau path lengkap tidak berfungsi.Perhatikan bahwa berjalan
strace
padaawk '1' foo=bar
serta memeriksa dengancat foo=bar
menunjukkan bahwa ini adalah masalah awk spesifik, dan execve melakukan acara nama file sebagai argumen berlalu, sehingga kerang tidak ada hubungannya dengan tugas variabel env dalam kasus ini.Selain itu, harap dicatat bahwa
awk '...script...' foo=bar
tidak akan menyebabkan pembuatan variabel lingkungan oleh shell, karena tugas variabel lingkungan harus mendahului perintah untuk berlaku. Lihat POSIX Shell Grammar Rules , poin nomor 7. Selain itu, ini dapat diverifikasi melaluiawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd
sumber