Bash

如何從 awk 中呼叫 bash 函式?

  • August 18, 2021

這有點棘手;我正在嘗試找出解決這個問題的最佳方法。我有幾種方法,但它們看起來真的很老套,我想要一些更優雅的東西。

我想解析一個空格分隔的文件,忽略#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 "$?"

引用自:https://unix.stackexchange.com/questions/242744