区切られた文字列をawkの配列に分割する方法は?


169

文字列にパイプ記号|が含まれている場合に文字列を分割する方法。それらを配列に分割します。

私は試した

echo "12:23:11" | awk '{split($0,a,":"); print a[3] a[2] a[1]}'

うまくいきます。文字列が次のようになっている場合、"12|23|11"それらを配列に分割するにはどうすればよいですか?


3
出力は、区切り記号なしで配列要素を連結していることに注意してください。代わりにで区切る場合OFSは、それらの間にコンマを挿入して、printそれらを別々の引数として表示します。
dubiousjim 2012

それとも、sedを使用することができます:echo "12:23:11" | sed "s/.*://"
雪解け

@slushy:コマンドは、質問者が必要とするものではありません。コマンド(echo "12:23:11" | sed "s/.*://")は、最後の ":"まで(およびそれを含む)まですべて削除し、 "11"のみを保持します...最後の番号を取得するように機能しますが、取得するには(読みにくい方法で)変更する必要があります2番目の数値など、awk(およびawkの分割)は、はるかにエレガントで読みやすくなっています。
Olivier Dulac

使用できる単一の文字で分割する必要がある場合cut
ccpizza

回答:


274

やってみました:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'

2
@Mohamed Saligh、Solarisを使用している場合は、文字列の長さを指定して/ usr / xpg4 / bin / awkを使用する必要があります。
Dimitre Radoulov、2011年

5
「私のために働いていない」。特に、エコーされた値と '|'で分割するように設定された分割の間にコロンがある場合??? 打ち間違え?皆さんお元気で。
シェルター、2011年

1
いくつかの構文説明でより良い。
Alston、2015

2
の3番目の引数splitは正規表現であり、|特別な記号であるため、エスケープする必要があるため、これはGNU awkでは機能しません。使用split($0, a, "\|")
WhiteWind 2017

1
@WhiteWind:「確実」に別の方法|charとして見や特殊記号の間にそれを置くことですされていない[]。すなわち、 split($0, a, "[|]") より良いよりも、このように#I「\ |」、いくつかのケースでは、特に正規表現のいくつかの変種として( perl対grep対..その他?)は "|"を持つことができます 文字通り解釈され、 "\ |" 反対ではなく正規表現の区切り文字として見られる... ymmv
Olivier Dulac

119

文字列を配列に分割するawkには、次の関数を使用しますsplit()

 awk '{split($0, a, ":")}'
 #           ^^  ^  ^^^
 #            |  |   |
 #       string  |   delimiter
 #               |
 #               array to store the pieces

セパレーターが指定されていない場合はFS、デフォルトのスペースであるを使用します。

$ awk '{split($0, a); print a[2]}' <<< "a:b c:d e"
c:d

セパレータを与えることができます、例えば:

$ awk '{split($0, a, ":"); print a[2]}' <<< "a:b c:d e"
b c

これは、FS次のように設定するのと同じです。

$ awk -F: '{split($0, a); print a[1]}' <<< "a:b c:d e"
b c

gawkでは、セパレータを正規表現として提供することもできます。

$ awk '{split($0, a, ":*"); print a[2]}' <<< "a:::b c::d e" #note multiple :
b c

さらに、4番目のパラメーターを使用して、各ステップの区切り文字を確認します。

$ awk '{split($0, a, ":*", sep); print a[2]; print sep[1]}' <<< "a:::b c::d e"
b c
:::

GNU awkのmanページを引用してみましょう:

split(string、array [、fieldsep [、seps]])

除算ストリング片には、により分離fieldsepとで片記憶アレイにセパレータ文字列をSEPSアレイ。最初のピースはに格納されarray[1]、2番目のピースはに格納されarray[2]ます。三番目の引数の文字列値fieldsepは、スプリットに説明する正規表現である文字列(同じくらいFSが入力レコードを分割する記述する正規表現することができます)。場合fieldsepが省略されている、の値FSが使用されます。split()作成された要素の数を返します。sepsgawk拡張でありseps[i]、区切り文字列はarray[i]array[i+1]。場合 fieldsepは単一のスペースです。その後、先頭の空白が入りseps[0]、末尾の空白がに入りますseps[n]。ここで、nは戻り値split()(つまり、配列内の要素の数)です。


通常のawkではなく、gnu awkを使用していることを述べてください(seps []に区切り文字を格納せず、その他の制限があります)
Olivier Dulac

17

より具体的にしてください!「動かない」とはどういう意味ですか?正確な出力(またはエラーメッセージ)、OS、およびawkバージョンを投稿します。

% awk -F\| '{
  for (i = 0; ++i <= NF;)
    print i, $i
  }' <<<'12|23|11'
1 12
2 23
3 11

または、splitを使用します。

% awk '{
  n = split($0, t, "|")
  for (i = 0; ++i <= n;)
    print i, t[i]
  }' <<<'12|23|11'
1 12
2 23
3 11

編集:Solaris では、4000フィールドを正しく処理するために、POSIX awk(/ usr / xpg4 / bin / awk)を使用する必要があります。


for(i = 0またはfor(i = 1
PiotrNycz 2015

i =0。これは、(i ++ではなく)後に++ iを使用するためです。
Dimitre Radoulov、2015

3
わかりました-私はこれに気づきませんでした。私はもっ​​と読みやすくなるだろうと強く信じていますfor (i = 1; i <= n; ++i)...
PiotrNycz

5

echo "..." | awk ...ソリューションが不要forkexecシステムコールを呼び出すため、このソリューションは好きではありません。

私は少しひねったディミターの解決策を好む

awk -F\| '{print $3 $2 $1}' <<<'12|23|11'

または少し短いバージョン:

awk -F\| '$0=$3 $2 $1' <<<'12|23|11'

この場合、出力レコードはまとめられて真の状態であるため、出力されます。

この特定のケースでは stdinリダイレクトを設定することでリダイレクトを回避できます。 内部変数:

awk -v T='12|23|11' 'BEGIN{split(T,a,"|");print a[3] a[2] a[1]}'

使った かなり長い間、 これは、内部の文字列操作によって管理できます。最初のケースでは、元の文字列は内部ターミネーターによって分割されます。2番目のケースでは、文字列には常に1文字の区切り文字で区切られた数字のペアが含まれていると想定されています。

T='12|23|11';echo -n ${T##*|};T=${T%|*};echo ${T#*|}${T%|*}
T='12|23|11';echo ${T:6}${T:3:2}${T:0:2}

すべての場合の結果は

112312

最終的な結果は、指定された印刷出力の例に関係なく、awk配列変数の参照になるはずだったと思います。しかし、あなたはあなたの最終結果を提供するために本当に簡単なbashケースを見逃しました。T = '12:23:11 '; echo $ {T //:}
Daniel Liston

@DanielListonあなたは正しいです!ありがとう!末尾の/がこのbash式に残っている可能性があることを知りませんでした...
TrueY

4

実際にawkは「入力フィールドセパレーター変数」リンクと呼ばれる機能があります。これがその使い方です。実際には配列ではありませんが、内部の$変数を使用しています。単純な文字列を分割する場合は簡単です。

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'

3
echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

うまくいくはずです。



1

冗談で?:)

いかがですか echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

これは私の出力です:

p2> echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
112312

結局うまくいっているようです。


文字列の長さが原因ですか?それ以来、私のストリングの長さは4000です。すべてのアイデア
Mohamed Saligh

1

私はこれが古い質問の一種であることを知っていますが、おそらく私のトリックのような誰かがいると思いました。特に、このソリューションは特定の数のアイテムに限定されないためです。

# Convert to an array
_ITEMS=($(echo "12|23|11" | tr '|' '\n'))

# Output array items
for _ITEM in "${_ITEMS[@]}"; do
  echo "Item: ${_ITEM}"
done

出力は次のようになります。

Item: 12
Item: 23
Item: 11
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.