Lewati variabel shell sebagai / pattern / to awk

59

Memiliki yang berikut di salah satu fungsi shell saya:

function _process () {
  awk -v l="$line" '
  BEGIN {p=0}
  /'"$1"'/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '
}

, jadi ketika dipanggil sebagai _process $arg, $argakan dilewati sebagai $1, dan digunakan sebagai pola pencarian. Ini bekerja seperti ini, karena shell mengembang $1di tempat pola awk! Juga ldapat digunakan di dalam program awk, sedang dideklarasikan dengan -v l="$line". Semua baik-baik saja

Apakah mungkin dengan cara yang sama memberikan pola untuk mencari sebagai variabel?

Mengikuti tidak akan berhasil,

awk -v l="$line" -v search="$pattern" '
  BEGIN {p=0}
  /search/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '

, sebagai awk tidak akan menafsirkan /search/sebagai variabel, tetapi secara harfiah.

dedak
sumber

Jawaban:

46

Gunakan ~operator awk , dan Anda tidak perlu memberikan regex literal di sisi kanan:

function _process () {
    awk -v l="$line" -v pattern="$1" '
        $0 ~ pattern {p=1} 
        END {if(p) print l >> "outfile.txt"}
    '  
}

Meskipun ini akan lebih efisien (tidak harus membaca seluruh file)

function _process () {
    grep -q "$1" && echo "$line"
}

Tergantung pada polanya, mungkin ingin grep -Eq "$1"

glenn jackman
sumber
Inilah yang memecahkan masalah ini dengan cara yang saya inginkan (contoh pertama), karena ini menjaga semantik, yang merupakan tujuan saya. Terima kasih.
branquito
1
Saya tidak mencatat penghapusan blok BEGIN: variabel yang tidak ditugaskan diperlakukan sebagai 0 dalam konteks numerik atau string kosong sebaliknya. Jadi, variabel yang tidak ditugaskan akan menjadi inif (p) ...
glenn jackman
ya saya perhatikan, itu perlu diatur pada BEGIN blok ke nol setiap kali, karena berfungsi sebagai saklar. Tapi yang menarik saya coba sekarang menggunakan script $0 ~ pattern, dan itu tidak berhasil, namun dengan /'"$1"'/itu tidak berfungsi !? : O
branquito
mungkin ada hubungannya dengan cara $linediambil, pencarian pola dilakukan pada output dari whois $line, $linedatang dari file di blok WHILE DO.
branquito
Tolong tunjukkan konten $line- lakukan di pertanyaan Anda untuk pemformatan yang tepat.
glenn jackman
17
awk  -v pattern="$1" '$0 ~ pattern'

Memiliki masalah dalam awkmemperluas urutan pelarian ANSI C (seperti \nuntuk baris baru, \funtuk umpan formulir, \\untuk backslash dan sebagainya) di $1. Jadi itu menjadi masalah jika $1mengandung karakter backslash yang umum dalam ekspresi reguler (dengan GNU awk4.2 atau lebih tinggi, nilai yang dimulai dengan @/dan diakhiri /, juga merupakan masalah ). Pendekatan lain yang tidak menderita dari masalah itu adalah menulisnya:

PATTERN=$1 awk '$0 ~ ENVIRON["PATTERN"]'

Seberapa buruk akan tergantung pada awkimplementasinya.

$ nawk -v 'a=\.' 'BEGIN {print a}'
.
$ mawk -v 'a=\.' 'BEGIN {print a}'
\.
$ gawk -v 'a=\.' 'BEGIN {print a}'
gawk: warning: escape sequence `\.' treated as plain `.'
.
$ gawk5.0.1 -v 'a=@/foo/' BEGIN {print a}'
foo

Semua awks bekerja sama untuk urutan pelarian yang valid:

$ a='\\-\b' awk 'BEGIN {print ENVIRON["a"]}' | od -tc
0000000   \   \   -   \   b  \n
0000006

(konten $alulus apa adanya)

$ awk -v a='\\-\b' 'BEGIN {print a}' | od -tc
0000000   \   -  \b  \n
0000004

( \\diubah menjadi \dan \bdiubah menjadi karakter backspace).

Stéphane Chazelas
sumber
Jadi Anda mengatakan bahwa jika pola misalnya \d{3}untuk menemukan tiga digit, itu tidak akan berfungsi seperti yang diharapkan, jika saya mengerti Anda dengan baik?
branquito
2
untuk \dyang bukan urutan pelarian C yang valid, itu tergantung pada awkimplementasi Anda (jalankan awk -v 'a=\d{3}' 'BEGIN{print a}'untuk memeriksa). Tetapi untuk \` or \ b , yes definitely. (BTW, I don't know of any awk implementations that understands \ d` artinya digit).
Stéphane Chazelas
dikatakan: awk warning - escape sequence \d' treated as plain d 'd {3}, jadi saya kira saya akan memiliki masalah dalam kasus ini?
branquito
1
Maaf, salah saya, saya punya kesalahan ketik dalam jawaban saya. Nama variabel lingkungan kemudian harus cocok ENVIRON["PATTERN"]dengan PATTERNvariabel lingkungan. Jika Anda ingin menggunakan variabel shell, Anda harus mengekspornya terlebih dahulu ( export variable) atau menggunakan ENV=VALUE awk '...ENVIRON["ENV"]'sintaks lewat env-var seperti dalam jawaban saya.
Stéphane Chazelas
1
Karena Anda perlu mengekspor variabel shell agar dapat diteruskan di lingkungan ke perintah.
Stéphane Chazelas
5

Coba sesuatu seperti:

awk -v l="$line" -v search="$pattern" 'BEGIN {p=0}; { if ( match( $0, search )) {p=1}}; END{ if(p) print l >> "outfile.txt" }'
Hunter Eidson
sumber
Jika ini berperilaku sama seperti /regex/dalam hal menemukan pola, ini bisa menjadi solusi yang bagus. Saya akan mencoba.
branquito
1
Tes cepat yang saya jalankan tampaknya bekerja sama, tetapi saya bahkan tidak akan mulai menjaminnya ... :)
Hunter Eidson
0

Tidak, tetapi Anda dapat dengan mudah menginterpolasikan pola tersebut ke dalam string yang dikutip ganda yang Anda lewatkan dengan awk:

awk -v l="$line" "BEGIN {p=0}; /$pattern/ {p=1}; END{ if(p) print l >> \"outfile.txt\" }"

Perhatikan bahwa Anda sekarang harus melarikan diri dari awk literal yang dikutip ganda, tetapi ini masih merupakan cara paling sederhana untuk mencapai ini.

Kilian Foth
sumber
Apakah ini aman jika $patternberisi spasi, contoh saya di atas akan berfungsi karena $ 1 dilindungi dengan tanda kutip ganda "$ 1", namun tidak tahu apa yang terjadi dalam kasus Anda.
branquito
2
Contoh asli Anda mengakhiri string dengan tanda kutip tunggal pada detik ', kemudian melindungi $1melalui tanda kutip ganda dan kemudian mengaitkan string dengan tanda kutip tunggal lainnya untuk paruh kedua program awk. Jika saya mengerti dengan benar, ini seharusnya memiliki efek yang persis sama dengan melindungi $1via tanda kutip tunggal luar - awk tidak pernah melihat tanda kutip ganda yang Anda tempatkan di sekitarnya.
Kilian Foth
4
Tetapi jika $patternmengandung ^/ {system("rm -rf /")};, maka Anda dalam masalah besar.
Stéphane Chazelas
apakah kelemahan pendekatan ini saja, setelah semuanya dibungkus ""?
branquito
-3

Anda bisa menggunakan fungsi eval yang menyelesaikan dalam contoh ini variabel jaring sebelum awk dijalankan.

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
Noxy
sumber