awk 'processing_script_here' my=file.txt
tampaknya berhenti dan menunggu tanpa batas waktu ...
Apa yang terjadi di sini dan bagaimana cara membuatnya berfungsi?
awk 'processing_script_here' my=file.txt
tampaknya berhenti dan menunggu tanpa batas waktu ...
Apa yang terjadi di sini dan bagaimana cara membuatnya berfungsi?
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 sebelum BEGIN
pernyataan) alih-alih memasukkan nama file input.
Itu bisa berguna dalam hal-hal seperti:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Di mana Anda dapat menentukan file yang berbeda FS
/ RS
per. Ini juga biasa digunakan di:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Yang merupakan versi lebih aman dari:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(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 =
adalah awk
nama variabel yang valid .
Apa yang merupakan nama variabel yang valid di awk
lebih ketat daripada di sh
.
POSIX membutuhkannya seperti:
[_a-zA-Z][_a-zA-Z0-9]*
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 suka Stéphane=Chazelas.txt
mungkin atau tidak mungkin, tergantung pada awk
implementasi 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 txt
file tidak akan mengandung =
karakter.
Juga, berhati-hatilah karena argumen seperti -vfoo=bar.txt
dapat dianggap sebagai opsi jika Anda menggunakan:
awk -f file.awk -vfoo=bar.txt
(juga berlaku untuk awk '{code}' -vfoo=bar.txt
dengan awk
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 sebaliknya awk
dipahami sebagai input standar berarti ).
Itu juga sebabnya
#! /usr/bin/awk -f
Shebang tidak bekerja. Sementara var=value
yang bisa dikerjakan dengan memperbaikiARGV
nilai - nilai (tambahkan ./
awalan) dalam sebuah BEGIN
pernyataan:
#! /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 awk
dan bukan awk
skrip.
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 awk
memperbaiki semua masalah tersebut dengan -E
opsinya.
Setelah -E
, gawk hanya mengharapkan path dari awk
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:
#! /usr/bin/gawk -E
shebang di mana daftar argumen selalu memasukkan file (perhatikan bahwa Anda masih dapat mengedit ARGV
daftar itu dalam sebuah BEGIN
pernyataan).
Anda juga dapat menggunakannya sebagai:
gawk -e '...awk code here...' -E /dev/null *.txt
Kami menggunakan -E
dengan skrip kosong ( /dev/null
) hanya untuk memastikan bahwa *.txt
setelah itu selalu diperlakukan sebagai file input, meskipun mengandung =
karakter.
../foo
, /path/to/foo
dan 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 ;-)
./
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 mengatasi awk
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 :
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):
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 gunakan my_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../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, dalam awk '{exit}' < /etc/passwd
, Anda akan berharap awk
untuk mencari kembali ke akhir catatan pertama setelah itu exit
untuk memastikan itu meninggalkan posisi dalam stdin di sana. POSIX mensyaratkan itu. /usr/xpg4/bin/awk
melakukannya di Solaris, tetapi tampaknya tidak gawk
juga mawk
melakukannya di GNU / Linux.
awk
cara itu.
Mengutip dokumentasi gawk (penekanan catatan ditambahkan):
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.txt
tidak ada file yang ditentukan oleh definisi di atas - my=file.txt
ditafsirkan sebagai penugasan variabel, dan jika tidak ada file yang didefinisikan awk
akan membaca stdin (juga jelas dari strace
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
nilai foo
dicetak untuk setiap baris di / etc / passwd. Menentukan./foo=bar
atau path lengkap tidak berfungsi.
Perhatikan bahwa berjalan strace
pada awk '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