ฉันจะใช้ตัวแปรเชลล์ในสคริปต์ awk ได้อย่างไร


290

ผมพบว่าวิธีการที่จะผ่านตัวแปรเปลือกภายนอกไปยังawkสคริปต์ แต่ฉันสับสนเกี่ยวกับและ'"

ก่อนอื่นฉันลองด้วย shell script:

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

จากนั้นลอง awk:

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

ทำไมถึงแตกต่าง

สุดท้ายฉันก็ลองทำสิ่งนี้:

$ 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 

ฉันสับสนเกี่ยวกับเรื่องนี้


2
ฉันชอบ -v ดังที่แสดงด้านล่าง แต่นี่เป็นแบบฝึกหัดที่ยอดเยี่ยมในการคิดเกี่ยวกับวิธีการปกป้องสิ่งต่าง ๆ จากเปลือกหอย เมื่อทำงานผ่านสิ่งนี้การตัดครั้งแรกของฉันจะใช้แบ็กสแลชในช่องว่างและเครื่องหมายดอลลาร์ ไม่จำเป็นต้องบอกว่าตัวอย่างที่นี่คุ้มค่ากับเวลาของฉัน
Chris


หากการค้นหา awk ของคุณต้องการการแสดงออกปกติคุณจะไม่สามารถใส่/var/ได้ แทนการใช้ตัวหนอน:awk -v var="$var" '$0 ~ var'
โนม Manos

คำตอบ:


497

กำลังรับตัวแปรเชลล์เข้า awk

อาจทำได้หลายวิธี บางคนดีกว่าคนอื่น สิ่งนี้ควรครอบคลุมส่วนใหญ่ หากคุณมีความคิดเห็นโปรดออกจากด้านล่าง v1.5


การใช้-v (วิธีที่ดีที่สุดพกพามากที่สุด)

ใช้-vตัวเลือก: (PS ใช้ช่องว่างหลังจากนั้น-vหรือจะพกพาได้น้อยลงเช่นawk -v var=ไม่ใช่awk -vvar=)

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

สิ่งนี้ควรเข้ากันได้กับส่วนใหญ่awkและตัวแปรมีอยู่ในBEGINบล็อกด้วย:

หากคุณมีหลายตัวแปร:

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

การเตือน อย่างที่เอ็ดมอร์ตันเขียนลำดับการหลบหนีจะถูกตีความดังนั้น\tจะกลายเป็นของจริงtabและไม่ใช่\tว่าเป็นสิ่งที่คุณค้นหา สามารถแก้ไขได้โดยใช้ENVIRON[]หรือเข้าถึงผ่านทางARGV[]

ป.ล.ถ้าคุณชอบแถบแนวตั้งสามเส้นเป็นตัวคั่น|||มันจะหนีไม่พ้นดังนั้นใช้-F"[|][|][|]"

ตัวอย่างการรับข้อมูลจากโปรแกรม / ฟังก์ชั่นอินน์ไปที่awk(ใช้วันที่นี่)

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

ตัวแปรหลังจากโค้ดบล็อก

ที่นี่เราได้รับตัวแปรหลังจากawkรหัส สิ่งนี้จะทำงานได้ดีตราบใดที่คุณไม่ต้องการตัวแปรในBEGINบล็อก:

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • การเพิ่มตัวแปรหลายตัว:

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

  • ด้วยวิธีนี้เรายังสามารถตั้งค่าตัวคั่นฟิลด์ที่แตกต่างกันFSสำหรับแต่ละไฟล์

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

  • ตัวแปรหลังจากบล็อกรหัสจะไม่ทำงานสำหรับBEGINบล็อก:

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


นี่สตริง

ตัวแปรยังสามารถเพิ่มให้กับการawkใช้สตริงที่นี่จากเปลือกหอยที่สนับสนุนพวกเขา (รวมถึง Bash):

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

นี่เป็นเช่นเดียวกับ:

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

ปล. นี้ถือว่าตัวแปรเป็นไฟล์อินพุต


ENVIRON อินพุต

ในฐานะที่เป็น TrueY เขียนคุณสามารถใช้ENVIRONในการพิมพ์ตัวแปรของสภาพแวดล้อม การตั้งค่าตัวแปรก่อนเรียกใช้ AWK คุณสามารถพิมพ์ออกมาดังนี้:

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

ARGV อินพุต

อย่างที่ Steven Penny เขียนคุณสามารถใช้ARGVรับข้อมูลเป็น awk:

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

ในการรับข้อมูลลงในโค้ดนั้นไม่ใช่แค่ BEGIN:

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

ตัวแปรภายในรหัส: ใช้ด้วยความระมัดระวัง

คุณสามารถใช้ตัวแปรภายในawkรหัส แต่มันยุ่งและอ่านยากและตามที่Charles Duffyชี้ไปรุ่นนี้อาจตกเป็นเหยื่อของการฉีดโค้ด หากมีคนเพิ่มสิ่งที่ไม่ดีให้กับตัวแปรมันจะถูกเรียกใช้งานเป็นส่วนหนึ่งของawkรหัส

สิ่งนี้ทำงานโดยการแยกตัวแปรภายในโค้ดดังนั้นมันจึงกลายเป็นส่วนหนึ่งของมัน

หากคุณต้องการทำการawkเปลี่ยนแปลงแบบไดนามิกด้วยการใช้ตัวแปรคุณสามารถทำได้ด้วยวิธีนี้ แต่อย่าใช้สำหรับตัวแปรปกติ

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

นี่คือตัวอย่างของการฉีดรหัส:

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

คุณสามารถเพิ่มคำสั่งมากมายawkด้วยวิธีนี้ แม้แต่ทำให้มันล้มเหลวด้วยคำสั่งที่ไม่ถูกต้อง


ข้อมูลเสริม:

ใช้เครื่องหมายคำพูดคู่

มันเป็นการดีเสมอที่"$variable"
จะใส่เครื่องหมายคำพูดคู่หากไม่ได้จะเพิ่มหลายบรรทัดเป็นบรรทัดเดียวที่ยาว

ตัวอย่าง:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

ข้อผิดพลาดอื่น ๆ ที่คุณจะได้รับโดยไม่ต้องใส่เครื่องหมายคำพูดคู่:

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

และด้วยเครื่องหมายคำพูดเดี่ยวมันไม่ได้เป็นการเพิ่มค่าของตัวแปร:

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

ข้อมูลเพิ่มเติมเกี่ยวกับ AWK และตัวแปร

อ่านคำถามที่พบบ่อยนี้


2
"ยุ่งและอ่านยาก" ไม่สนใจข้อกังวลด้านความปลอดภัยที่สำคัญกว่าของการฉีดโค้ดเมื่อแทนที่สตริงลงในโค้ด awk โดยตรง
Charles Duffy

การอ่านคำตอบข้างต้นฉันสามารถเรียกใช้สคริปต์โดยไม่มีข้อผิดพลาด แต่ไม่ทำงาน: awk -v repo = "$ 1" -v tag = "$ 2" '{sub (/ image: registryabx.azurecr.io \ / { พิมพ์ repo}: ([a-z0-9] +) $ /, "image: registryabc.azurecr. io / {print repo}: {print tag}");} 1 './services/appscompose.yaml >> newcompose.yaml เป็นเพราะวงเล็บที่ซ้อนกัน {?
Darion Badlydone

@DarionBadlydone awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'ลองนี้ มันจะดูว่ามันพิมพ์ตัวแปร โพสต์คำถามของตัวเองถ้าคุณไม่สามารถคิดออก
Jotne

@Jotne ใช่มันพิมพ์ค่าดังนั้นฉันพยายามด้วยวิธีนี้: awk -v repo = "$ 1" -v tag = "$ 2" '{พิมพ์ "{sub (/ ภาพ: registryabc.azurecr.io/"repo" :( [a-z0-9] +) $ /, \ "ภาพ: registryabc.azurecr.io/"repo":" tag"\");}1"} './services/appscompose.yaml >> newcompose.yaml แต่ใช้งานไม่ได้ มันแทนที่แต่ละบรรทัดของไฟล์ต้นฉบับด้วยคำสั่งที่พิมพ์
Darion Badlydone

@Jotne ฉันทำมันด้วย sed ขอบคุณแล้ว
Darion Badlydone

28

ดูเหมือนว่าคนดี ENVIRON hash ในตัวไม่ได้กล่าวถึงเลย ตัวอย่างของการใช้งาน:

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

4
นี่เป็นข้อเสนอแนะที่ดีเพราะผ่านคำต่อคำทุกคำ -vไม่ทำงานเมื่อค่ามีแบ็กสแลช
ผู้ชายคนอื่นนั้น

2
@thatotherguy ฉันไม่รู้ว่า! ฉันคิดว่าถ้าฉันใช้awk -v x='\c\d' ...มันก็จะใช้ได้อย่างถูกต้อง แต่เมื่อxมีการพิมพ์awkหยดที่มีชื่อเสียง: awk: warning: escape sequence '\c' treated as plain 'c'ข้อความผิดพลาด ... ขอบคุณ!
TrueY

มันทำงานอย่างถูกต้อง - อย่างถูกต้องในบริบทนี้หมายถึงการขยายลำดับการยกเว้นเนื่องจากเป็นวิธีที่-vออกแบบมาเพื่อให้คุณสามารถใช้\tในตัวแปรและทำให้มันตรงกับแท็บตัวอักษรในข้อมูลตัวอย่างเช่น หากที่ไม่พฤติกรรมที่คุณต้องการแล้วคุณไม่ได้ใช้-vคุณใช้หรือARGV[] ENVIRON[]
Ed Morton

9

ใช้อย่างใดอย่างหนึ่งเหล่านี้ขึ้นอยู่กับวิธีที่คุณต้องการแบ็กสแลชในการจัดการตัวแปรเชลล์ ( avarเป็นตัวแปร awk svarเป็นตัวแปรเชลล์):

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

ดูhttp://cfajohnson.com/shell/cus-faq-2.html#Q24สำหรับรายละเอียดและตัวเลือกอื่น ๆ วิธีแรกข้างต้นมักเป็นตัวเลือกที่ดีที่สุดของคุณและมีความหมายชัดเจนที่สุด


6

คุณสามารถส่งผ่านตัวเลือกบรรทัดคำสั่ง -vด้วยชื่อตัวแปร ( v) และค่า ( =) ของตัวแปรสภาพแวดล้อม ( "${v}"):

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

หรือเพื่อให้ชัดเจนยิ่งขึ้น (ที่มีน้อยกว่าvs):

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

3

คุณสามารถใช้ ARGV:

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

โปรดทราบว่าหากคุณจะเข้าสู่ร่างกายต่อไปคุณจะต้องปรับ ARGC:

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

1

ฉันเพิ่งเปลี่ยนคำตอบของ @Jotne สำหรับ "for loop"

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

1
นี่เป็นเพียงภาพประกอบอีกวิธีในการใช้-vตัวเลือกของ Awk ซึ่งได้กล่าวถึงแล้วในคำตอบที่มีอยู่มากมาย หากคุณต้องการแสดงวิธีเรียกใช้ Awk ในลูปนั่นเป็นคำถามที่แตกต่างออกไปจริงๆ
tripleee

0

ฉันต้องแทรกวันที่ที่จุดเริ่มต้นของบรรทัดของไฟล์บันทึกและมันทำเหมือนด้านล่าง:

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

สามารถเปลี่ยนเส้นทางไปยังไฟล์อื่นเพื่อบันทึก


การเสนอราคาสองครั้ง - การเสนอราคาเดี่ยว - การเสนอราคาซ้ำเป็นสิ่งที่ฉันต้องการในการทำงานของฉัน
user53029

2
นี่ถูกกล่าวถึงแล้วในคำตอบที่ยอมรับว่าเป็นวิธีที่คุณไม่ควรใช้เนื่องจากช่องโหว่การฉีดโค้ด ดังนั้นข้อมูลที่นี่ซ้ำซ้อน (อธิบายไว้แล้วในคำตอบที่ยอมรับ) และไม่สมบูรณ์ (ไม่ได้พูดถึงปัญหาด้วยวิธีนี้)
Jason S
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.