これは少しトリッキーです。この問題を解決するための最良の方法を見つけようとしています。いくつかの方法がありますが、本当に昔ながらのように見え、よりエレガントな方法が欲しいです。
#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
bash
機能awkで - 入力ファイルのコメントではなく、各行に1回。
どうすればいいですか?
答え1
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
5番目の行は正しく終了します。
while
ループがパイプのターゲットであるため、サブシェルで実行されることを指摘する価値があります。できない親スクリプトの環境(環境変数を含む)を変更します。
必要に応じてパイプを使用せず、リダイレクトとプロセス置換を使用してください。
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 "$?"
答え2
casの答えは素晴らしいですが、実際にawkから出力を再解析する必要があり、最初のawkコマンドで実行したい場合は、awkで素晴らしいパイプコマンド構文を使用できます。
awk '
{
cmd = "echo name:tag:url:method" # (very simple example)
while (cmd | getline)
{
#process output ($0)
print
}
close(cmd)
}
'