Bagaimana cara menggunakan variabel shell dalam skrip awk?

289

Saya menemukan beberapa cara untuk mengirimkan variabel shell eksternal ke awkskrip, tetapi saya bingung tentang 'dan ".

Pertama, saya mencoba dengan skrip shell:

$ v=123test
$ echo $v
123test
$ echo "$v"
123test

Kemudian mencoba awk:

$ awk 'BEGIN{print "'$v'"}'
$ 123test
$ awk 'BEGIN{print '"$v"'}'
$ 123

Kenapa bedanya?

Terakhir saya mencoba ini:

$ awk 'BEGIN{print " '$v' "}'
$  123test
$ awk 'BEGIN{print ' "$v" '}'
awk: cmd. line:1: BEGIN{print
awk: cmd. line:1:             ^ unexpected newline or end of string 

Saya bingung tentang ini.

hqjma
sumber
2
Saya suka -v seperti yang ditunjukkan di bawah ini, tetapi ini benar-benar latihan yang bagus dalam memikirkan bagaimana melindungi sesuatu dari cangkang. Bekerja melalui ini, potongan pertama saya menggunakan garis miring terbalik pada spasi dan tanda dolar. Tak perlu dikatakan contoh-contoh di sini sepadan dengan waktu saya.
Chris
Jika pencarian awk Anda membutuhkan ekspresi reguler , Anda tidak bisa memasukkan /var/. Sebagai gantinya, gunakan tilde:awk -v var="$var" '$0 ~ var'
Noam Manos

Jawaban:

496

Mendapatkan variabel shell awk

dapat dilakukan dengan beberapa cara. Beberapa lebih baik dari yang lain. Ini harus mencakup sebagian besar dari mereka. Jika Anda memiliki komentar, silakan tinggalkan di bawah ini. v1.5


Menggunakan -v (Cara terbaik, paling portabel)

Gunakan -vopsi: (PS gunakan spasi setelah -vatau akan lebih portabel. Misalnya, awk -v var=tidak awk -vvar=)

variable="line one\nline two"
awk -v var="$variable" 'BEGIN {print var}'
line one
line two

Ini harus kompatibel dengan sebagian besar awk, dan variabel juga tersedia di BEGINblok:

Jika Anda memiliki banyak variabel:

awk -v a="$var1" -v b="$var2" 'BEGIN {print a,b}'

Peringatan . Seperti yang ditulis Ed Morton, urutan pelarian akan ditafsirkan sehingga \tmenjadi nyata tabdan bukan \tjika itu yang Anda cari. Dapat diatasi dengan menggunakan ENVIRON[]atau mengaksesnya melaluiARGV[]

PS Jika Anda suka tiga batang vertikal sebagai pemisah |||, ia tidak dapat diloloskan, jadi gunakan-F"[|][|][|]"

Contoh untuk mendapatkan data dari program / fungsi penginapan ke awk (tanggal digunakan)

awk -v time="$(date +"%F %H:%M" -d '-1 minute')" 'BEGIN {print time}'

Variabel setelah blok kode

Di sini kita mendapatkan variabel setelah awkkode. Ini akan berfungsi dengan baik selama Anda tidak perlu variabel dalamBEGIN blok:

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • Menambahkan beberapa variabel:

awk '{print a,b,$0}' a="$var1" b="$var2" file

  • Dengan cara ini kita juga dapat mengatur Pemisah Bidang yang berbeda FSuntuk setiap file.

awk 'some code' FS=',' file1.txt FS=';' file2.ext

  • Variabel setelah blok kode tidak akan berfungsi untuk BEGINblok:

echo "input data" | awk 'BEGIN {print var}' var="${variable}"


Sini-string

Variabel juga dapat ditambahkan untuk awkmenggunakan string di sini dari shell yang mendukungnya (termasuk Bash):

awk '{print $0}' <<< "$variable"
test

Ini sama dengan:

printf '%s' "$variable" | awk '{print $0}'

PS ini memperlakukan variabel sebagai input file.


ENVIRON memasukkan

Saat TrueY menulis, Anda dapat menggunakan ENVIRONuntuk mencetak Variabel Lingkungan . Mengatur variabel sebelum menjalankan AWK, Anda dapat mencetaknya seperti ini:

X=MyVar
awk 'BEGIN{print ENVIRON["X"],ENVIRON["SHELL"]}'
MyVar /bin/bash

ARGV memasukkan

Seperti yang ditulis Steven Penny, Anda dapat menggunakan ARGVuntuk membuat data menjadi awk:

v="my data"
awk 'BEGIN {print ARGV[1]}' "$v"
my data

Untuk mendapatkan data ke dalam kode itu sendiri, bukan hanya BEGIN:

v="my data"
echo "test" | awk 'BEGIN{var=ARGV[1];ARGV[1]=""} {print var, $0}' "$v"
my data test

Variabel dalam kode: GUNAKAN DENGAN HATI-HATI

Anda dapat menggunakan variabel dalam awkkode, tetapi berantakan dan sulit dibaca, dan seperti yang Charles Duffyditunjukkan, versi ini juga bisa menjadi korban injeksi kode. Jika seseorang menambahkan hal-hal buruk ke variabel, itu akan dieksekusi sebagai bagian dari awkkode.

Ini berfungsi dengan mengekstraksi variabel dalam kode, sehingga menjadi bagian darinya.

Jika Anda ingin membuat awkyang berubah secara dinamis dengan menggunakan variabel, Anda dapat melakukannya dengan cara ini, tetapi JANGAN menggunakannya untuk variabel normal.

variable="line one\nline two"
awk 'BEGIN {print "'"$variable"'"}'
line one
line two

Berikut adalah contoh injeksi kode:

variable='line one\nline two" ; for (i=1;i<=1000;++i) print i"'
awk 'BEGIN {print "'"$variable"'"}'
line one
line two
1
2
3
.
.
1000

Anda dapat menambahkan banyak perintah dengan awkcara ini. Bahkan membuatnya crash dengan perintah yang tidak valid.


Informasi tambahan:

Penggunaan kutipan ganda

Itu selalu baik untuk menggandakan variabel penawaran. "$variable"
Jika tidak, beberapa baris akan ditambahkan sebagai satu baris panjang.

Contoh:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

Kesalahan lain yang bisa Anda dapatkan tanpa penawaran ganda:

variable="line one\nline two"
awk -v var=$variable 'BEGIN {print var}'
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ backslash not last character on line
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ syntax error

Dan dengan kutipan tunggal, itu tidak memperluas nilai variabel:

awk -v var='$variable' 'BEGIN {print var}'
$variable

Info selengkapnya tentang AWK dan variabel

Baca faq ini .

Jotne
sumber
2
"Berantakan dan sulit dibaca" mengabaikan masalah keamanan yang lebih penting dari injeksi kode ketika langsung mengganti string menjadi kode awk.
Charles Duffy
membaca jawaban di atas saya dapat menjalankan skrip saya tanpa kesalahan tetapi tidak berhasil: awk -v repo = "$ 1" -v tag = "$ 2" '{sub (/ image: registryabx.azurecr.io \ / { print repo}: ([a-z0-9] +) $ /, "image: registryabc.azurecr. io / {print repo}: {print tag}");} 1 './services/appscompose.yaml >> newcompose.yaml. Apakah karena kurung bersarang {?
Darion Badlydone
@DarionBadlydone Coba ini awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'. Ini akan melihat apakah ia mencetak variabel. Posting pertanyaan sendiri jika Anda tidak bisa mengetahuinya.
Jotne
@Jotne ya itu mencetak nilai jadi saya mencoba dengan cara ini: awk -v repo = "$ 1" -v tag = "$ 2" '{print "{sub (/ image: registryabc.azurecr.io/"repo" :( [a-z0-9] +) $ /, \ "image: registryabc.azurecr.io/"repo":"tag"\");}1"} './services/appscompose.yaml >> newcompose.yaml tetapi tidak berfungsi seperti yang diharapkan. Ia mengganti setiap baris file sumber dengan perintah tercetak
Darion Badlydone
@Jotne, saya melakukannya dengan sed, Terima kasih
Darion Badlydone
28

Tampaknya baik-tua ENVIRON built-in hash tidak disebutkan sama sekali. Contoh penggunaannya:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt
Benar
sumber
4
Ini adalah saran yang bagus karena melewati data kata demi kata. -vtidak berfungsi saat nilainya berisi garis miring terbalik.
pria lain itu
2
@thatotherguy saya tidak tahu itu! Saya pikir jika saya gunakan awk -v x='\c\d' ...maka akan digunakan dengan benar. Tetapi ketika xdicetak awk menjatuhkan yang terkenal: awk: warning: escape sequence '\c' treated as plain 'c'pesan kesalahan ... Terima kasih!
Benar
Itu berfungsi dengan baik - benar dalam konteks ini berarti memperluas urutan pelarian karena itulah cara -vdirancang untuk bekerja sehingga Anda dapat menggunakan \tdalam variabel dan membuatnya cocok dengan tab literal dalam data, misalnya. Jika itu bukan perilaku yang Anda inginkan maka Anda tidak menggunakan yang -vAnda gunakan ARGV[]atau ENVIRON[].
Ed Morton
9

Gunakan salah satu dari ini tergantung bagaimana Anda ingin garis miring terbalik pada variabel shell yang ditangani ( avaradalah variabel awk, svaradalah variabel shell):

awk -v avar="$svar" '... avar ...' file
awk 'BEGIN{avar=ARGV[1];ARGV[1]=""}... avar ...' "$svar" file

Lihat http://cfajohnson.com/shell/cus-faq-2.html#Q24 untuk detail dan opsi lainnya. Metode pertama di atas hampir selalu merupakan pilihan terbaik Anda dan memiliki semantik yang paling jelas.

Ed Morton
sumber
6

Anda bisa meneruskan opsi baris perintah -v dengan nama variabel ( v) dan nilai ( =) variabel lingkungan ( "${v}"):

% awk -vv="${v}" 'BEGIN { print v }'
123test

Atau untuk membuatnya lebih jelas (dengan jauh lebih sedikit v):

% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test
Johnsyweb
sumber
3

Anda dapat menggunakan ARGV:

v=123test
awk 'BEGIN {print ARGV[1]}' "$v"

Perhatikan bahwa jika Anda akan melanjutkan ke tubuh, Anda perlu menyesuaikan ARGC:

awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"
Steven Penny
sumber
1

Saya baru saja mengubah jawaban @ Jotne untuk "for loop".

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done
edib
sumber
1
Ini sepertinya hanyalah ilustrasi lain tentang cara menggunakan -vopsi Awk yang telah disebutkan dalam banyak jawaban yang ada. Jika Anda ingin menunjukkan cara menjalankan Awk dalam satu lingkaran, itu pertanyaan yang sangat berbeda.
tripleee
0

Saya harus memasukkan tanggal di awal baris file log dan dilakukan seperti di bawah ini:

DATE=$(date +"%Y-%m-%d")
awk '{ print "'"$DATE"'", $0; }' /path_to_log_file/log_file.log

Itu dapat diarahkan ke file lain untuk menyimpan

Sina
sumber
Kutipan ganda - kutipan tunggal - kutipan ganda adalah persis apa yang saya butuhkan untuk membuat pekerjaan tambang.
user53029
2
Ini sudah disebutkan dalam jawaban yang diterima sebagai metode yang tidak boleh Anda gunakan karena kerentanan injeksi kode. Jadi informasi di sini berlebihan (sudah dijelaskan dalam jawaban yang diterima), dan tidak lengkap (tidak menyebutkan masalah dengan metode ini).
Jason S