ส่งผ่านตัวแปรเชลล์เป็น / pattern / ไปยัง awk


59

มีดังต่อไปนี้ในหนึ่งในฟังก์ชั่นเปลือกของฉัน:

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

ดังนั้นเมื่อเรียกว่าเป็น_process $arg, $argได้รับการส่งผ่านเป็น$1และนำมาใช้เป็นรูปแบบการค้นหา มันทำงานได้ด้วยวิธีนี้เพราะเชลล์ขยาย$1แทนรูปแบบ awk! นอกจากนี้ยังlสามารถนำมาใช้ภายในโปรแกรม awk -v l="$line"ถูกประกาศด้วย ทุกอย่างดี

เป็นไปได้หรือไม่ที่จะให้รูปแบบการค้นหาเป็นตัวแปร

การติดตามจะไม่ทำงาน

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

ในฐานะ awk จะไม่ตีความ/search/ว่าเป็นตัวแปร แต่เป็นตัวอักษรแทน

คำตอบ:


46

ใช้~โอเปอเรเตอร์ของ awk และคุณไม่จำเป็นต้องจัดทำ regex ตามตัวอักษรทางด้านขวา:

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

แม้ว่ามันจะมีประสิทธิภาพมากกว่า (ไม่ต้องอ่านไฟล์ทั้งหมด)

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

ขึ้นอยู่กับรูปแบบอาจต้องการ grep -Eq "$1"


ตรงนี้เป็นสิ่งที่แก้ได้ในแบบที่ฉันต้องการ (ตัวอย่างที่ 1) เพราะมันเก็บความหมายไว้ซึ่งเป็นเป้าหมายของฉัน ขอบคุณ
branquito

1
ฉันไม่ได้สังเกตว่าการลบบล็อก BEGIN: ตัวแปรที่ไม่ได้กำหนดจะถือว่าเป็น 0 ในบริบทที่เป็นตัวเลขหรือสตริงว่าง ดังนั้นตัวแปรที่ไม่ได้กำหนดจะเป็นเท็จในif (p) ...
เกล็นแจ็คแมน

ใช่ฉันสังเกตเห็นว่าจะต้องตั้งค่าในบล็อก BEGIN เป็นศูนย์ทุกครั้งเนื่องจากเป็นสวิตช์ แต่ที่น่าสนใจตอนนี้ฉันลองใช้สคริปต์$0 ~ patternและมันใช้งานไม่ได้ แต่/'"$1"'/มันใช้งานได้! : O
branquito

บางทีมันอาจจะมีสิ่งที่จะทำอย่างไรกับวิธีการที่$lineถูกดึงรูปแบบการค้นหาจะทำในการส่งออกของwhois $line, $lineมาจากแฟ้มในขณะที่ทำบล็อก
branquito

โปรดแสดงเนื้อหาของ$line- ทำในคำถามของคุณสำหรับการจัดรูปแบบที่เหมาะสม
glenn jackman

17
awk  -v pattern="$1" '$0 ~ pattern'

มีปัญหาในการที่awkจะขยายลำดับการหลบหนีของ ANSI C (เช่น\nสำหรับการขึ้นบรรทัดใหม่\fสำหรับการฟีดแบบฟอร์ม\\สำหรับแบ็กสแลชและอื่น ๆ ) $1มา ดังนั้นจึงกลายเป็นปัญหาหาก$1มีอักขระเครื่องหมายทับขวาซึ่งเป็นเรื่องปกติในนิพจน์ทั่วไป (ที่มี GNU awk4.2 หรือสูงกว่าค่าที่ขึ้นต้นด้วย@/และสิ้นสุด/ก็เป็นปัญหาด้วย ) วิธีการอื่นที่ไม่ประสบปัญหานั้นคือการเขียน:

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

มันจะแย่แค่ไหนนั้นขึ้นอยู่กับawkการนำไปใช้งาน

$ 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

ทั้งหมดawkทำงานเหมือนกันสำหรับลำดับ escape ที่ถูกต้องแม้ว่า:

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

(เนื้อหาของการ$aส่งผ่านตามที่เป็นอยู่)

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

( \\เปลี่ยนเป็น\และ\bเปลี่ยนเป็นอักขระถอยกลับ)


คุณกำลังบอกว่าถ้ารูปแบบเป็นตัวอย่าง\d{3}เพื่อค้นหาตัวเลขสามหลักนั่นจะไม่ทำงานอย่างที่คาดไว้ถ้าฉันเข้าใจคุณดี
branquito

2
สำหรับการ\dที่ไม่ได้เป็นลำดับ C หลบหนีที่ถูกต้องที่ขึ้นอยู่กับคุณawkการดำเนินงาน (เรียกใช้awk -v 'a=\d{3}' 'BEGIN{print a}'ในการตรวจสอบ) แต่สำหรับ\` or \ b , yes definitely. (BTW, I don't know of any awk implementations that understands \ d` หมายถึงหลัก)
Stéphane Chazelas

มันบอกว่า: awk warning - escape sequence \d' treated as plain d 'd {3} ดังนั้นฉันเดาว่าฉันจะมีปัญหาในกรณีนี้หรือไม่
branquito

1
ขอโทษฉันไม่ดีฉันพิมพ์ผิดในคำตอบของฉัน ชื่อของตัวแปรสภาพแวดล้อมนั้นต้องตรงกันENVIRON["PATTERN"]สำหรับPATTERNตัวแปรสภาพแวดล้อม หากคุณต้องการใช้ตัวแปรเชลล์คุณต้องส่งออกก่อน ( export variable) หรือใช้ENV=VALUE awk '...ENVIRON["ENV"]'ไวยากรณ์การส่งผ่าน env-var เช่นเดียวกับในคำตอบของฉัน
Stéphane Chazelas

1
เพราะคุณต้องส่งออกตัวแปรเชลล์เพื่อให้ส่งผ่านในสภาพแวดล้อมไปยังคำสั่ง
Stéphane Chazelas

5

ลองสิ่งที่ชอบ:

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

หากสิ่งนี้ทำหน้าที่เหมือนกับ/regex/ในแง่ของการค้นหารูปแบบนี่อาจเป็นทางออกที่ดี ฉันจะพยายาม.
branquito

1
การทดสอบอย่างรวดเร็วที่ฉันวิ่งดูเหมือนจะทำงานเหมือนเดิม แต่ฉันจะไม่เริ่มรับประกัน ... :)
Hunter Eidson

0

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

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

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


วิธีนี้ปลอดภัยไหมถ้า$patternมีการเว้นวรรคตัวอย่างของฉันจากด้านบนจะทำงานในขณะที่ $ 1 ได้รับการปกป้องด้วยเครื่องหมายคำพูด "$ 1" แต่ไม่ต้องดูว่าเกิดอะไรขึ้นในกรณีของคุณ
branquito

2
ตัวอย่างดั้งเดิมของคุณจบด้วยสตริงที่มีเครื่องหมายคำพูดเดี่ยวในวินาที'จากนั้นปกป้อง$1คำพูดผ่านคู่และจากนั้นจะสตริงสตริงที่ยกมาเดี่ยวอีกอันสำหรับครึ่งหลังของโปรแกรม awk หากฉันเข้าใจอย่างถูกต้องสิ่งนี้ควรมีผลเช่นเดียวกันกับการปกป้อง$1ผ่านเครื่องหมายคำพูดเดี่ยวด้านนอก - awk ไม่เคยเห็นเครื่องหมายคำพูดคู่ที่คุณใส่ไว้
Kilian Foth

4
แต่ถ้า$patternมี^/ {system("rm -rf /")};แล้วคุณมีปัญหาใหญ่
Stéphane Chazelas

ข้อเสียของวิธีนี้เท่านั้นมีทั้งหมดห่อด้วย ""?
branquito

-3

คุณสามารถใช้ฟังก์ชัน eval ซึ่งแก้ไขในตัวอย่างนี้ตัวแปร nets ก่อนที่จะรัน awk

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.