Bir awk betiğinde kabuk değişkenlerini nasıl kullanabilirim?


290

Dış kabuk değişkenlerini bir awkkomut dosyasına geçirmenin bazı yollarını buldum , ancak 've hakkında kafam karıştı ".

İlk olarak, bir kabuk komut dosyası ile denedim:

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

Sonra awk denedi:

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

Fark neden?

Son olarak bunu denedim:

$ 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 

Bu konuda kafam karıştı.


2
Aşağıda gösterildiği gibi -v'yi seviyorum, ancak bu, şeyleri kabuktan nasıl koruyacağınızı düşünmek için gerçekten harika bir egzersiz. Bu şekilde çalışırken, ilk kesimim boşluklarda ve dolar işaretlerinde ters eğik çizgiler kullanmaktadır. Söylemeye gerek yok, buradaki örnekler zamanımın değerinde.
Chris


Awk aramanızın düzenli ifadeye ihtiyacı varsa, koyamazsınız /var/. Bunun yerine tilde kullanın:awk -v var="$var" '$0 ~ var'
Noam Manos

Yanıtlar:


497

Kabuk değişkenlerini içine alma awk

birkaç şekilde yapılabilir. Bazıları diğerlerinden daha iyidir. Bu onların çoğunu kapsamalıdır. Eğer bir yorumunuz varsa, lütfen aşağıya bırakın. v1.5


Kullanma -v (en iyi yolu, en taşınabilir)

-vSeçeneği kullanın : (PS -vdaha sonra bir boşluk kullanırsa veya daha az taşınabilir olacaktır. Örneğin, awk -v var=değil awk -vvar=)

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

Bu çoğu ile uyumlu olmalı awkve değişken BEGINblokta da kullanılabilir :

Birden çok değişkeniniz varsa:

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

Uyarı . Ed Morton'un yazdığı gibi, kaçış dizileri yorumlanacak, böylece aradığınız şey bu değil , \tgerçek olur . Kullanarak çözülebilir veya üzerinden erişilebilirtab\tENVIRON[]ARGV[]

PS Eğer ayırıcı olarak üç dikey çubuk isterseniz |||, kaçamaz, bu yüzden kullanın-F"[|][|][|]"

Bir program / işlev hanından veri alma örneği awk(burada tarih kullanılır)

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

Kod bloğundan sonra değişken

Burada değişkeni awkkoddan sonra alırız . Bu, BEGINbloktaki değişkene ihtiyacınız olmadığı sürece iyi çalışır :

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • Birden çok değişken ekleme:

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

  • Bu şekilde FSher dosya için farklı Alan Ayırıcı da ayarlayabiliriz .

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

  • Kod bloğundan sonraki değişken blok için çalışmaz BEGIN:

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


İşte-string

Değişken, onları destekleyen kabuklardan (Bash dahil) burada bir dizeninawk kullanılması için de eklenebilir :

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

Bu aynı:

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

PS, bu değişkeni bir dosya girişi olarak ele alır.


ENVIRON giriş

TrueY yazarken, Ortam DeğişkenleriniENVIRON yazdırmak için kullanabilirsiniz . AWK'yi çalıştırmadan önce bir değişken ayarlamak, onu şu şekilde yazdırabilirsiniz:

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

ARGV giriş

Steven Penny'in yazdığı gibi, ARGVverileri awk'ye almak için kullanabilirsiniz :

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

Verileri kodun içine almak için sadece BEGIN değil:

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

Kod içindeki değişken: DİKKATLİ KULLANIN

awkKod içinde bir değişken kullanabilirsiniz , ancak dağınık ve okunması zor ve belirtildiği gibi Charles Duffy, bu sürüm kod enjeksiyonunun bir kurbanı olabilir. Birisi değişkene kötü şeyler eklerse, awkkodun bir parçası olarak yürütülür .

Bu, kod içindeki değişkeni ayıklayarak çalışır, böylece bir parçası haline gelir.

awkDeğişkenlerin kullanımı ile dinamik olarak değişen bir değişiklik yapmak istiyorsanız, bunu bu şekilde yapabilirsiniz, ancak normal değişkenler için KULLANMAYIN.

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

Kod enjeksiyonuna bir örnek:

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

awkBu şekilde çok sayıda komut ekleyebilirsiniz . Hatta geçerli olmayan komutlarla çökmesini sağlayın.


Fazladan bilgi:

Çift tırnak kullanımı

Değişken değerini iki katına çıkarmak her zaman iyidir. "$variable"
Değilse, uzun bir satır olarak birden çok satır eklenir.

Misal:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

Çift tırnak işareti olmadan alabileceğiniz diğer hatalar:

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

Ve tek tırnakla, değişkenin değerini genişletmez:

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

AWK ve değişkenler hakkında daha fazla bilgi

Bu SSS'yi okuyun .


2
"dağınık ve okunması zor", dizeleri doğrudan awk koduyla değiştirirken kod enjeksiyonunun daha önemli güvenlik endişesini göz ardı eder.
Charles Duffy

Yukarıdaki cevabı okurken betiğimi hatasız çalıştırabilirim ama bu işi yapmıyor: awk -v repo = "$ 1" -v tag = "$ 2" '{sub (/ image: registryabx.azurecr.io \ / { Repo yazdır}: ([a-z0-9] +) $ /, "image: registerryabc.azurecr. io / {print repo}: {print tag}");} 1 './services/appscompose.yaml >> newcompose.yaml. İç içe geçmiş parantez nedeniyle mi {?
Darion Badlydone

@DarionBadlydone Bunu deneyin awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'. Değişkeni yazdırıp yazdırmayacağını görecektir. Bunu çözemiyorsanız, kendi sorunuzu gönderin.
Jotne

@Jotne evet bu şekilde denedim değerleri yazdırın: 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 ama istendiği gibi çalışmıyor. Kaynak dosyanın her satırını basılı komutla değiştirir
Darion Badlydone

@Jotne sed ile yaptım, yine de teşekkürler
Darion Badlydone

28

Görünüşe göre, iyi yaşlı ENVIRON yerleşik karma değerinden hiç bahsedilmez. Kullanımına bir örnek:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt

4
Bu iyi bir öneri çünkü veriyi kelimesi kelimesine aktarıyor. -vdeğer ters eğik çizgi içerdiğinde çalışmaz.
diğer adam

2
@thatotherguy Bunu bilmiyordum! Ben kullanırsam awk -v x='\c\d' ...o zaman düzgün kullanılacağını düşündüm . Ama xbasıldığında awk ünlü düşer: awk: warning: escape sequence '\c' treated as plain 'c'hata mesajı ... Teşekkürler!
True

Düzgün çalışır - bu bağlamda düzgün bir şekilde kaçış dizilerini genişletmek anlamına gelir, çünkü bu -vşekilde çalışmak için tasarlanmıştır, böylece \tdeğişkente kullanabilirsiniz ve örneğin verilerdeki bir değişmez sekme ile eşleşmesini sağlayabilirsiniz. İstediğiniz davranış bu değilse veya -vkullanmazsınız . ARGV[]ENVIRON[]
Ed Morton

9

İşlenen kabuk değişkenlerindeki ters eğik çizgileri nasıl istediğinize bağlı olarak bunlardan birini kullanın ( avarbir awk değişkeni, svarbir kabuk değişkeni):

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

Ayrıntılar ve diğer seçenekler için http://cfajohnson.com/shell/cus-faq-2.html#Q24 adresine bakın . Yukarıdaki ilk yöntem neredeyse her zaman en iyi seçeneğinizdir ve en belirgin semantiğe sahiptir.


6

Ortam değişkeni ( ) değişken adı ( ) ve değeri ( ) ile komut satırı seçeneğini iletebilirsiniz :-vv="${v}"

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

Veya daha net yapmak için (çok daha vazıyla):

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

3

ARGV'yi kullanabilirsiniz:

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

Vücuda devam edecekseniz, ARGC'yi ayarlamanız gerekeceğini unutmayın:

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

1

@ Jotne'un "for loop" cevabını değiştirdim.

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done

1
Bu sadece -vmevcut cevapların çoğunda daha önce bahsedilen Awk seçeneğinin nasıl kullanılacağının başka bir örneği gibi görünüyor . Awk'ı bir döngüde nasıl çalıştıracağınızı göstermek istiyorsanız, bu gerçekten farklı bir soru.
tripleee

0

Bir günlük dosyasının satırlarının başına tarih eklemek zorunda kaldı ve aşağıdaki gibi yapılır:

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

Kaydetmek için başka bir dosyaya yönlendirilebilir


Çift teklif - tek teklif - çift teklif tam olarak benim iş yapmak için gerekli oldu.
user53029

2
Bu, kod yanıtlama güvenlik açıkları nedeniyle kullanmamanız gereken bir yöntem olarak kabul edilen yanıtta zaten belirtilmiştir. Yani buradaki bilgiler gereksizdir (kabul edilen cevapta zaten açıklanmıştır) ve eksiktir (bu yöntemdeki problemlerden bahsetmez).
Jason S
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.