web-dev-qa-db-ja.com

forループ内のawkコマンド

成功しませんでしたが、awkループ内でforコマンドを使用しようとしました。

データを取得するためにawkで切り取りたい一連の文字列を含む変数があります。

私はそれを行う方法を知っていますが、私が本当に欲しいのは、データを連続してカットすることです。

だから私はこの変数を持っています:

var="data1,data2,data3"

そして私が今ここにいるところ:

for ((i=1; i<=3; i++))
do
    echo $(awk -F, '{print $1}' <<< $var)
done

$1ループによって$iですが、成功していません。

2
Amargein

Awkスクリプトで二重引用符を使用してシェル変数を挿入することで、実行しようとしていることを実行できます。それでも、1つのリテラル$を保持したい場合は、バックスラッシュでエスケープすることによって実行できます。

echo $(awk -F, "{print \$$i}" <<<$var)

これにより、各反復で$i123に展開されます。したがって、awkは各フィールドを展開する$1$2$3を参照します。

別の可能性は、-vフラグを使用してシェル変数をawk変数として注入することです:

echo $(awk -F, -v i="$i" '{print $i}' <<<$var)

これにより、awk変数iが同じ名前のシェル変数の内容に割り当てられます。 awkの変数は、フィールドに使用される$を使用しないため、iがawkの変数の場合、$ii-thフィールドを参照できます。

-vを使用してawk変数を割り当てることは、特に任意の文字シーケンスを含めることができる場合、一般に安全なアプローチです。その場合、意図に反してコンテンツがawkコードとして実行されるリスクが少なくなります。しかし、あなたの場合、変数は単一の整数を保持しているので、それは問題ではありません。

さらに別のオプションは、awk自体でforループを使用することです。その方法の詳細については、awkのドキュメントを参照(またはこのサイトを検索)してください。

5
filbranden

awk は、j(変数として)と$j(フィールドインデックスとして)の両方を受け入れることができます。

for i in 1 2 3; do echo "$var" | awk -v j=$i -F , '{print $j}'; done

"混乱" awkの例では$iどちらを使用するか(Shellまたはその独自の変数-優先)両方が$プレフィックスで参照されるため。

注意

sh 「ポータブル」スクリプティングの標準であるシェルはサポートしていません:

(( i=1; i<=3; i++; ))および<<< $var構成

また、可能であれば、seqコマンドをforループで使用して、番号シーケンス生成をより細かく制御することを検討してください。

1
user86041

この状況ではawkの使用は過度に思われますが、trとwhileループはどうでしょうか。

tr , '\n' <<<"$var" | while read; do
  echo $REPLY
done

出力:

data1
data2
data3
1
Thor
#!/bin/sh

var='data1,data2,data3'

unset data
while [ "$var" != "$data" ]; do
    data=${var%%,*}    # delete first comma and the bit after it
    var=${var#*,}      # delete bit up to first comma (and the comma)

    printf 'data = "%s"\n' "$data"
done

ここでは、変数置換を使用して、var変数の値からコンマ区切りの連続する各データフィールドを取得します。ループ内のdataへの最初の割り当ては、最初のコンマの後の$varからすべてを削除します。次に、var変数が変更され、最初のコンマまでの最初のビットが削除されます。

これは"$var" = "$data"まで続きます。これは、文字列に対してこれ以上何もできないことを意味します。

この方法で、改行が埋め込まれたコンマ区切りのデータ文字列を処理できます。

var='line1
line2,data2,last bit
goes here'

上記の値がvarにある場合、上記のスクリプトは

data = "line1
line2"
data = "data2"
data = "last bit
goes here"

埋め込まれた改行を気にしません。 めったにないawkの呼び出しをループする必要があります。

awkは、文字列をコンマ区切りのフィールドのセットとして完全に読み取り、これらをループできることに注意してください。

printf '%s\n' "$var" |
awk -F ',' '{ for (i=1; i<=NF; i++) print $i }'

var='data1,data2,data3'を使用すると、これは印刷されます

data1
data2
data3

IFS変数を使用して$var値をビットに分割し、set -fを使用してファイル名の展開を無効にする別のシェルソリューション:

set -f
oldIFS=$IFS; IFS=','

set -- $var

IFS=$oldIFS; unset oldIFS
set +f

for data do
    printf 'data = "%s"\n' "$data"
done
0
Kusalananda