8

bashで文字列を区切り文字列に分割し、パーツを配列に配置する方法を探しています。

単純なケース:

#!/bin/bash
b="aaaaa/bbbbb/ddd/ffffff"
echo "simple string: $b"

IFS='/' b_split=($b)
echo ;
echo "split"
for i in ${b_split[@]}
do
    echo "------ new part ------"
    echo "$i"
done

出力を出します

simple string: aaaaa/bbbbb/ddd/ffffff

split
------ new part ------
aaaaa
------ new part ------
bbbbb
------ new part ------
ddd
------ new part ------
ffffff

より複雑なケース:

#!/bin/bash
c=$(echo "AA=A"; echo "B=BB"; echo "======="; echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF";)
echo "more complex string"
echo "$c";
echo ;
echo "split";

IFS='=======' c_split=($c) ;#    <----    LINE TO BE CHANGED 

for i in ${c_split[@]}
do
    echo "------ new part ------"
    echo "$i"
done

出力を提供します:

more complex string
AA=A
B=BB
=======
C==CC
DD=D
=======
EEE
FF

split
------ new part ------
AA
------ new part ------
A
B
------ new part ------
BB

------ new part ------

------ new part ------

------ new part ------

------ new part ------

------ new part ------

------ new part ------

------ new part ------

C
------ new part ------

------ new part ------
CC
DD
------ new part ------
D

------ new part ------

------ new part ------

------ new part ------

------ new part ------

------ new part ------

------ new part ------

------ new part ------

EEE
FF

2番目の出力を次のようにしたいと思います

------ new part ------
AA=A
B=BB
------ new part ------
C==CC
DD=D
------ new part ------
EEE
FF

つまり、文字列を1つではなく一連の文字で分割します。これどうやってするの?

私は2番目のスクリプトでこの行を変更するだけの答えを探しています:

IFS='=======' c_split=($c) ;#    <----    LINE TO BE CHANGED 
4

5 に答える 5

19

IFS曖昧性解消

IFSとして、入力フィールドセパレータを意味しlist of characters that could be used as separatorsます。

デフォルトでは、これはに設定されています。これは 、スペース集計、および/または改行 \t\nの任意の数(ゼロより大きい)を1にすることができることを意味します。 separator

したがって、文字列:

 "    blah  foo=bar 
 baz  "

先頭と末尾の区切り文字は無視され、この文字列には、、、の3つの部分のみが含まblahfoo=barますbaz

IFS文字列で使用されていない有効なフィールドセパレータがわかっている場合は、を使用して文字列を分割できます。

OIFS="$IFS"
IFS='§'
c=$'AA=A\nB=BB\n=======\nC==CC\nDD=D\n=======\nEEE\nFF'
c_split=(${c//=======/§})
IFS="$OIFS"
printf -- "------ new part ------\n%s\n" "${c_split[@]}"

------ new part ------
AA=A
B=BB

------ new part ------

C==CC
DD=D

------ new part ------

EEE
FF

ただし、これは文字列にが含まれていない場合にのみ機能します§

のように別のキャラクターを使用することもできますIFS=$'\026';c_split=(${c//=======/$'\026'})が、とにかくこれにはさらにバグが含まれる可能性があります。

文字列に含まれていないものを見つけるために文字コード表を参照できます。

myIfs=""
for i in {1..255};do
    printf -v char "$(printf "\\\%03o" $i)"
        [ "$c" == "${c#*$char}" ] && myIfs="$char" && break
  done
if ! [ "$myIFS" ] ;then
    echo no split char found, could not do the job, sorry.
    exit 1
  fi

しかし、私はこの解決策が少しやり過ぎだと思います。

スペースでの分割(またはIFSを変更せずに)

の下では、次のbashismを使用できます。

b="aaaaa/bbbbb/ddd/ffffff"
b_split=(${b//// })

実際、この構文は、配列に割り当てる前に、すべての出現箇所をスペースで置き換える${varname//変換(で区切られる)を開始します。//b_split

もちろん、これはまだIFSスペースで配列を使用して分割します。

これは最善の方法ではありませんが、特定のケースで機能する可能性があります。

分割する前に不要なスペースを削除することもできます。

b='12 34 / 1 3 5 7 / ab'
b1=${b// }
b_split=(${b1//// })
printf "<%s>, " "${b_split[@]}" ;echo
<12>, <34>, <1>, <3>, <5>, <7>, <ab>, 

またはそれらを交換します。

b1=${b// /§}
b_split=(${b1//// })
printf "<%s>, " "${b_split[@]//§/ }" ;echo
<12 34 >, < 1 3 5 7 >, < ab>, 

上の分割線strings

したがって、自分の意味で使用する必要はありませんが、 には優れた機能があります。IFS

#!/bin/bash

c=$'AA=A\nB=BB\n=======\nC==CC\nDD=D\n=======\nEEE\nFF'
echo "more complex string"
echo "$c";
echo ;
echo "split";

mySep='======='
while [ "$c" != "${c#*$mySep}" ];do
    echo "------ new part ------"
    echo "${c%%$mySep*}"
    c="${c#*$mySep}"
  done
echo "------ last part ------"
echo "$c"

見てみましょう:

more complex string
AA=A
B=BB
=======
C==CC
DD=D
=======
EEE
FF

split
------ new part ------
AA=A
B=BB

------ new part ------

C==CC
DD=D

------ last part ------

EEE
FF

注:先頭と末尾の改行は削除されません。これが必要な場合は、次のことができます。

mySep=$'\n=======\n'

単にの代わりに=======

または、これを明示的に排除するために分割ループを書き直すことができます。

mySep=$'======='
while [ "$c" != "${c#*$mySep}" ];do
    echo "------ new part ------"
    part="${c%%$mySep*}"
    part="${part##$'\n'}"
    echo "${part%%$'\n'}"
    c="${c#*$mySep}"
  done
echo "------ last part ------"
c=${c##$'\n'}
echo "${c%%$'\n'}"

いずれにせよ、これはSOの質問が求めたものと一致します(:と彼のサンプル:)

------ new part ------
AA=A
B=BB
------ new part ------
C==CC
DD=D
------ last part ------
EEE
FF

最終的に作成するarray

#!/bin/bash
c=$'AA=A\nB=BB\n=======\nC==CC\nDD=D\n=======\nEEE\nFF'
echo "more complex string"
echo "$c";
echo ;
echo "split";

mySep=$'======='
export -a c_split
while [ "$c" != "${c#*$mySep}" ];do
    part="${c%%$mySep*}"
    part="${part##$'\n'}"
    c_split+=("${part%%$'\n'}")
    c="${c#*$mySep}"
  done
c=${c##$'\n'}
c_split+=("${c%%$'\n'}")

for i in "${c_split[@]}"
do
    echo "------ new part ------"
    echo "$i"
done

これを細かく行います:

more complex string
AA=A
B=BB
=======
C==CC
DD=D
=======
EEE
FF

split
------ new part ------
AA=A
B=BB
------ new part ------
C==CC
DD=D
------ new part ------
EEE
FF

いくつかの説明:

  • export -a var配列として定義varし、それらを子で共有します
  • ${variablename%string*}${variablename%%string*}結果は変数名の左側になりますが、文字列は含まれません。文字列の最後の出現を%意味し、すべての出現に対して。完全な変数名が返されますが、文字列が見つかりません。%%
  • ${variablename#*string}、逆の方法で同じことを行います。変数名の最後の部分を文字列から返しますが、文字列は返しません。1つは最初の発生#を意味し、2人はすべての発生を意味します。##

代わりに、文字*ジョーカーであり、任意の数の任意の文字を意味します。

このコマンドは変数cecho "${c%%$'\n'}"をエコーし​​ますが、文字列の最後に改行はありません。

したがって、変数にが含まれている場合Hello WorldZorGluBHello youZorGluBI'm happy

variable="Hello WorldZorGluBHello youZorGluBI'm happy"

$ echo ${variable#*ZorGluB}
Hello youZorGlubI'm happy

$ echo ${variable##*ZorGluB}
I'm happy

$ echo ${variable%ZorGluB*}
Hello WorldZorGluBHello you

$ echo ${variable%%ZorGluB*}
Hello World

$ echo ${variable%%ZorGluB}
Hello WorldZorGluBHello youZorGluBI'm happy

$ echo ${variable%happy}
Hello WorldZorGluBHello youZorGluBI'm

$ echo ${variable##* }
happy

これはすべて、マンページで説明されています。

$ man -Len -Pless\ +/##word bash

$ man -Len -Pless\ +/%%word bash

$ man -Len -Pless\ +/^\\\ *export\\\ .*word bash

ステップバイステップ、分割ループ:

セパレータ:

mySep=$'======='

配列c_splitとして宣言する(そして子と共有することができます)

export -a c_split

変数cには、少なくとも1つのオカレンスが含まれていますがmySep

while [ "$c" != "${c#*$mySep}" ];do

cを文字列の最初から最後まで切り捨てmySep、に割り当てpartます。

    part="${c%%$mySep*}"

主要な改行を削除する

    part="${part##$'\n'}"

末尾の改行を削除し、結果を新しい配列要素としてに追加しc_splitます。

    c_split+=("${part%%$'\n'}")

そのままにしておくと、残りの弦を取り除いてcを解放しますmySep

    c="${c#*$mySep}"

終わり ;-)

done

主要な改行を削除する

c=${c##$'\n'}

末尾の改行を削除し、結果を新しい配列要素としてに追加しc_splitます。

c_split+=("${c%%$'\n'}")

関数に:

ssplit() {
    local string="$1" array=${2:-ssplited_array} delim="${3:- }" pos=0
    while [ "$string" != "${string#*$delim}" ];do
        printf -v $array[pos++] "%s" "${string%%$delim*}"
        string="${string#*$delim}"
      done
    printf -v $array[pos] "%s" "$string"
}

使用法:

ssplit "<quoted string>" [array name] [delimiter string]

ここで、配列名$splitted_arrayデフォルトであり、区切り文字は1つの単一スペースです。

あなたが使用することができます:

c=$'AA=A\nB=BB\n=======\nC==CC\nDD=D\n=======\nEEE\nFF'
ssplit "$c" c_split $'\n=======\n'
printf -- "--- part ----\n%s\n" "${c_split[@]}"
--- part ----
AA=A
B=BB
--- part ----
C==CC
DD=D
--- part ----
EEE
FF
于 2013-02-09T15:18:16.293 に答える
3

awkでそれを行う:

 awk -vRS='\n=*\n'  '{print "----- new part -----";print}' <<< $c

出力:

kent$  awk -vRS='\n=*\n'  '{print "----- new part -----";print}' <<< $c
----- new part -----
AA=A
B=BB
----- new part -----
C==CC
DD=D
----- new part -----
EEE
FF
于 2013-01-31T17:01:23.147 に答える
1

bashでテストされた次のスクリプト:

kent@7pLaptop:/tmp/test$ bash --version
GNU bash, version 4.2.42(2)-release (i686-pc-linux-gnu)

スクリプト:(名前付きt.sh

#!/bin/bash

c=$(echo "AA=A"; echo "B=BB"; echo "======="; echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF";)
echo "more complex string"
echo "$c"
echo "split now"

c_split=($(echo "$c"|awk -vRS="\n=*\n"  '{gsub(/\n/,"\\n");printf $0" "}'))

for i in ${c_split[@]}
do
    echo "---- new part ----"
    echo -e "$i" 
done

出力:

kent@7pLaptop:/tmp/test$ ./t.sh 
more complex string
AA=A
B=BB
=======
C==CC
DD=D
=======
EEE
FF
split now
---- new part ----
AA=A
B=BB
---- new part ----
C==CC
DD=D
---- new part ----
EEE
FF

そのforループのechoステートメントに注意してください。オプションを削除すると、次の-eように表示されます。

---- new part ----
AA=A\nB=BB
---- new part ----
C==CC\nDD=D
---- new part ----
EEE\nFF\n

取る-eかどうかはあなたの要件に依存します。

于 2013-02-04T10:20:01.753 に答える
1

データにリテラルのバックスラッシュシーケンス、スペースなどが含まれている場合に失敗しないアプローチは次のとおりです。

c=$(echo "AA=A"; echo "B=BB"; echo "======="; echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF";)
echo "more complex string"
echo "$c";
echo ;
echo "split";

c_split=()
while IFS= read -r -d '' part
do
  c_split+=( "$part" )
done < <(printf "%s" "$c" | sed -e 's/=======/\x00/g')
c_split+=( "$part" )

for i in "${c_split[@]}"
do
    echo "------ new part ------"
    echo "$i"
done

文字列は実際には要求に応じて"======="で分割されるため、改行はデータの一部になります( "echo"が独自の行を追加すると余分な空白行が発生します)。

于 2013-02-06T00:05:01.347 に答える
1

このコメントのために、サンプルテキストにいくつか追加しました。

これは、AA=AをAA=AまたはAA=\ nAに置き換えると壊れます–その他の人

編集:テキスト内の区切り文字に影響されない提案を追加しました。ただし、これはOPが要求していた「1行分割」を使用していませんが、bashで実行し、結果を配列にしたい場合は、これを実行する必要があります。

script.sh(新規):

#!/bin/bash

text=$(
  echo "AA=A"; echo "AA =A"; echo "AA=\nA"; echo "B=BB"; echo "=======";
  echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF";
)
echo "more complex string"
echo "$text"
echo "split now"

c_split[0]=""
current=""
del=""
ind=0

# newline
newl=$'\n'

# Save IFS (not necessary when run as sub shell)
saveIFS="$IFS"
IFS="$newl"
for row in $text; do

  if [[ $row =~ ^=+$ ]]; then
    c_split[$ind]="$current"
    ((ind++))
    current=""
    # Avoid preceding newline
    del=""
    continue
  fi

  current+="$del$row"
  del="$newl"
done

# Restore IFS
IFS="$saveIFS"

# If there is a last poor part of the text
if [[ -n $current ]]; then
  c_split[$ind]="$current"
fi

# The result is an array
for i in "${c_split[@]}"
do
    echo "---- new part ----"
    echo "$i"
done

script.sh(OLD、 "one line split"):( @Kent
のawkを使用してアイデアをスツールし、少し調整しました)

#!/bin/bash

c=$(
  echo "AA=A"; echo "AA =A"; echo "AA=\nA"; echo "B=BB"; echo "=======";
  echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF";
)
echo "more complex string"
echo "$c"
echo "split now"

# Now, this will be almost absolute secure,
# perhaps except a direct hit by lightning.
del=""
for ch in $'\1' $'\2' $'\3' $'\4' $'\5' $'\6' $'\7'; do
  if [ -z "`echo "$c" | grep "$ch"`" ]; then
    del="$ch"
    break
  fi
done

if [ -z "$del" ]; then
  echo "Sorry, all this testing but no delmiter to use..."
  exit 1
fi

IFS="$del" c_split=($(echo "$c" | awk -vRS="\n=+\n" -vORS="$del" '1'))

for i in ${c_split[@]}
do
  echo "---- new part ----"
  echo "$i"
done

出力:

[244an]$ bash --version
GNU bash, version 4.2.24(1)-release (x86_64-pc-linux-gnu)

[244an]$ ./script.sh
more complex string
AA=A
AA =A
AA=\nA
B=BB
=======
C==CC
DD=D
=======
EEE
FF
split now
---- new part ----
AA=A
AA =A
AA=\nA
B=BB
---- new part ----
C==CC
DD=D
---- new part ----
EEE
FF

改行をしないようにするために、を使用していませ-eechoAA=\\nA

于 2013-02-06T18:31:32.830 に答える