bashで文字列を配列に分割する方法

bashで文字列を配列に分割する方法

プログラムの出力に問題があります。bash でコマンドを起動し、その出力 (文字列) を取得して分割し、特定の場所に新しい行を追加する必要があります。文字列は次のようになります。

battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500

基本的にはxxx.yy.zz:値ですが、値にはスペースが含まれている可能性があります。これが私が得たい出力です

battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500 

最初のドットを検索し、その位置から戻って新しい行を配置するスペースを探すというアイデアがありますが、Bash でそれを実現する方法がわかりません。

答え1

純粋な bash ソリューション。文字列の処理に外部ツールは使用されず、パラメータ拡張のみが使用されます。

#! /bin/bash
str='battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500'

IFS=: read -a fields <<< "$str"

for (( i=0 ; i < ${#fields[@]} ; i++ )) ; do
    f=${fields[i]}

    notfirst=$(( i>0 ))
    last=$(( i+1 == ${#fields[@]} ))

    (( notfirst )) && echo -n ${f% *}

    start=('' $'\n' ' ')
    colon=('' ': ')
    echo -n "${start[notfirst + last]}${f##* }${colon[!last]}"
done
echo

説明:$notfirstおよび は$lastブール値です。最初のフィールドには、最後のスペースの前の部分${f% *}は印刷されません。そのようなものは存在しないからです。$startおよび は、$colonフィールドを区切るさまざまな文字列を保持します。最初の項目では、notfirst + lastは 0 なので、先頭に何も追加されません。残りの行では、$notfirstは 1 なので、改行が印刷されます。最後の行では、加算すると 2 になるため、スペースが印刷されます。次に、最後のスペースの後の部分が印刷されます${f##* }。最後の行を除くすべての行でコロンが印刷されます。

答え2

GNU sed を使用すると、連続する各文字列 (つまり、空白なし) を で終了し:、最初の文字列を除くすべての文字列の前に改行を配置できます。

sed 's/[^[:space:]]\+:/\n&/g2'

sedのバージョンが拡張機能をサポートしていない場合は、単純な修飾子gnを使用できます。g

sed 's/[^[:space:]]\{1,\}:/\
&/g'

これは、最初のキーの前に追加の改行を印刷することを除いて、同じように動作します。perl -pe 's/\S+:/\n$&/g'同じ条件で を使用することもできます (GNU sed に相当する Perl があるかもしれませg2んが、私は知りません)。

答え3

解決策perl

$ perl -pe 's{\S+:}{$seen++ ? "\n$&" : "$&"}ge' file
battery.charge: 90 
battery.charge.low: 30 
battery.runtime: 3690 
battery.voltage: 230.0 
device.mfr: MGE UPS SYSTEMS 
device.model: Pulsar Evolution 500

説明

  • \S+:文字列の末尾に一致します:
  • ("\n$&")最初の文字列を除くすべての一致した文字列の前に改行を挿入します($seen++)

答え4

入力内のタブと改行 (存在する場合) がプレーンスペースに変換されることを気にしないと仮定すると、機能する単純なアプローチがここにあります。

アイデアはシンプルです。入力を空白で分割し、:改行で終わるトークンを先頭に追加する (そして他のトークンの前にスペースを再追加する) ことを除いて、すべてのトークンを出力します。変数$countと関連は、最初の空行を防ぐためだけに有用です。問題がなければ削除できます。(スクリプトは、入力が現在のディレクトリにあるifというファイルにあると想定しています。)intput

#! /bin/bash

count=0
for i in $(<input) ; do
   fmt=
   if [[ $i =~ :$ ]] ; then
       if [[ $count -gt 0 ]] ; then
           fmt="\n%s"
       else
           fmt="%s"
       fi
       ((count++))
   else
       fmt=" %s"
   fi
   printf "$fmt" "$i"
done
echo
echo "Num items: $count"

誰かがもっと良い代替案を考え出してくれることを願っています。

$ cat input
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
$ ./t.sh
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Num items: 6

関連情報