Bash
如何從 awk 中呼叫 bash 函式?
這有點棘手;我正在嘗試找出解決這個問題的最佳方法。我有幾種方法,但它們看起來真的很老套,我想要一些更優雅的東西。
我想解析一個空格分隔的文件,忽略
#comment lines
並抱怨任何不包含 4 個欄位的非空行。這很容易awk
:awk '/^#/ {next}; NF == 0 {next}; NF != 4 {exit 1}; (dostuff)'
訣竅是我想對數據做什麼,實際上是將其設置為變數,
bash
然後執行一個bash
函式,除非 $2 包含特定值。這是一些虛擬碼(大部分是真實但混合的語言)來解釋我的意思:
# awk /^#/ {next} NF == 0 {next} NF != 4 {exit 1} $2 == "manual" {next} # bash NAME=$1 METHOD=$2 URL=$3 TAG=$4 complicated_bash_function_that_calls_lots_of_external_commands # then magically parse the next line with awk.
如果沒有一些醜陋的解決方法,我不知道如何做到這一點,例如為文件的每一行呼叫
awk
或單獨呼叫。sed
(最初我提出的問題是“如何從 awk 中呼叫 bash 函式或從 bash 中呼叫 awk 的每個輸出行?”)可能會將 bash 函式修改為自己的腳本,並使其接受上述參數 1、2、3、4。不過,我不確定如何從 awk 中呼叫它;因此我的問題標題。
我實際上更願意做的是將整個內容放在一個文件中,並使其成為一個 bash 腳本——
awk
從內部呼叫bash
而不是bash
從內部呼叫awk
。但是我仍然需要從 awk 中呼叫該bash
函式——對輸入文件的每個非註釋行呼叫一次。我怎樣才能做到這一點?
您可以通過管道
awk
的輸出進入while read
循環來做您想做的事情。例如:awk '/^#/ {next}; NF == 0 {next}; NF != 4 {exit 1} ; {print}' | while read -r NAME METHOD URL TAG ; do : # do stuff with $NAME, $METHOD, $URL, $TAG echo "$NAME:$METHOD:$URL:$TAG" done if [ "$PIPESTATUS" -eq 1 ] ; then : # do something to handle awk's exit code fi
經測試:
$ cat input.txt # comment NAME METHOD URL TAG a b c d 1 2 3 4 x y z a b c d $ ./testawk.sh input.txt NAME:METHOD:URL:TAG a:b:c:d 1:2:3:4
請注意,它在第五個
x y z
輸入行正確退出。值得指出的是,由於
while
循環是管道的目標,它在子 shell 中執行,因此無法更改其父腳本的環境(包括環境變數)。如果需要,請不要使用管道,而是使用重定向和程序替換:
while read -r NAME METHOD URL TAG ; do : # do stuff with $NAME, $METHOD, $URL, $TAG echo "$NAME:$METHOD:$URL:$TAG" done < <(awk '(/^#/ || NF == 0) {next}; NF != 4 { printf "%s:%s:Wrong number of fields\n", FILENAME, NR > "/dev/stderr"; exit 1 }; {print}' input.txt) # getting the exit code from the <(...) requires bash 4.4 or newer: wait $! if [ "$?" -ne 0 ] ; then : # something went wrong in the process substitution, deal with it fi
或者,您可以使用
coproc
內置在後台執行 awk 腳本作為協同程序:# By default, array var $COPROC holds the co-process' stdout and # stdin file descriptors. See `help coproc`. coproc { awk '(/^#/ || NF == 0) {next}; NF != 4 { printf "%s:%s:Wrong number of fields\n", FILENAME, NR > "/dev/stderr"; exit 1 }; {print}' input.txt } awkpid="$!" #declare -p COPROC # uncomment to see the FDs while read -r NAME METHOD URL TAG ; do echo "$NAME:$METHOD:$URL:$TAG" done <&"${COPROC[0]}" wait "$awkpid" echo "$?"