文字列を分割するサンプルスクリプトを作成しましたが、期待どおりに動作しません
#!/bin/bash
IN="One-XX-X-17.0.0"
IFS='-' read -r -a ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
echo "Element:$i"
done
#split 17.0.0 into NUM
IFS='.' read -a array <<<${ADDR[3]};
for element in "${array[@]}"
do
echo "Num:$element"
done
出力
One
XX
X
17.0.0
17 0 0
しかし、私は出力が次のようになると予想しました:
One
XX
X
17.0.0
17
0
0
修正してください(背景については S。Chazelasの回答も参照 を参照してください)。
#!/bin/bash
IN="One-XX-X-17.0.0"
IFS='-' read -r -a ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
if [ "$i" = "${i//.}" ] ; then
echo "Element:$i"
continue
fi
# split 17.0.0 into NUM
IFS='.' read -a array <<< "$i"
for element in "${array[@]}" ; do
echo "Num:$element"
done
done
出力:
Element:One
Element:XX
Element:X
Num:17
Num:0
Num:0
ノート:
条件付き2ndループin1stループを配置することをお勧めします。
bash
パターン置換("${i//.}"
)は、要素に.
があるかどうかを確認します。 (case
ステートメントは[~~~~ op [〜#〜]のコードに似ていませんが、より単純かもしれません。)
read
ing $array
を入力することによる<<< "${ADDR[3]}"
は、<<< "$i"
よりも一般的ではありません。 which要素に.
sがあることを知る必要はありません。
コードは、 "Element:17.0."の印刷が意図的ではないと想定しています。その動作isが意図されている場合は、メインループを次のように置き換えます。
for i in "${ADDR[@]}"; do
echo "Element:$i"
if [ "$i" != "${i//.}" ] ; then
# split 17.0.0 into NUM
IFS='.' read -a array <<< "$i"
for element in "${array[@]}" ; do
echo "Num:$element"
done
fi
done
bash
の古いバージョンでは、<<<
の後に変数を引用する必要がありました。これは4.4で修正されました。古いバージョンでは、変数はIFSで分割され、結果の単語はその<<<
リダイレクトを構成する一時ファイルに格納される前にスペースで結合されました。
4.2以前では、read
やcommand
のようなビルトインをリダイレクトすると、その分割はそのビルトインのIFSを使用することさえありました(4.3で修正されました):
$ bash-4.2 -c 'a=a.b.c.d; IFS=. read x <<< $a; echo "$x"'
a b c d
$ bash-4.2 -c 'a=a.b.c.d; IFS=. cat <<< $a'
a.b.c.d
$ bash-4.2 -c 'a=a.b.c.d; IFS=. command cat <<< $a'
a b c d
4.3で修正されたもの:
$ bash-4.3 -c 'a=a.b.c.d; IFS=. read x <<< $a; echo "$x"'
a.b.c.d
ただし、$a
は引き続きWordで分割されます。
$ bash-4.3 -c 'a=a.b.c.d; IFS=.; read x <<< $a; echo "$x"'
a b c d
4.4では:
$ bash-4.4 -c 'a=a.b.c.d; IFS=.; read x <<< $a; echo "$x"'
a.b.c.d
古いバージョンへの移植性のために、変数を引用します(またはzsh
を使用します(その<<<
は最初からのものであり、問題はありません))。
$ bash-any-version -c 'a=a.b.c.d; IFS=.; read x <<< "$a"; echo "$x"'
a.b.c.d
文字列を分割するこのアプローチは、改行文字を含まない文字列に対してのみ機能することに注意してください。また、a..b.c.
は"a"
、""
、"b"
、"c"
(空の最後の要素なし)に分割されることにも注意してください。
任意の文字列を分割するには、代わりにsplit + glob演算子を使用できます(これにより、標準になり、<<<
のように一時ファイルに変数の内容を保存する必要がなくなります)。
var='a.new
line..b.c.'
set -o noglob # disable glob
IFS=.
set -- $var'' # split+glob
for i do
printf 'item: <%s>\n' "$i"
done
または:
array=($var'') # in shells with array support
''
は、空の要素がある場合にそれを保持するためのものです。また、空の$var
を1つの空の要素に分割します。
または、適切な分割演算子を使用してシェルを使用します。
zsh
:
array=(${(s:.:)var} # removes empty elements
array=("${(@s:.:)var}") # preserves empty elements
rc
:
array = ``(.){printf %s $var} # removes empty elements
fish
set array (string split . -- $var) # not for multiline $var
awkを使用すると、1行の費用がかかります。
IN="One-XX-X-17.0.0"
awk -F'[-.]' '{ for(i=1;i<=NF;i++) printf "%s : %s\n",($i~/^[0-9]+$/?"Num":"Element"),$i }' <<<"$IN"
-F'[-.]'
-複数の文字に基づいたフィールドセパレータ、この場合は-
および.
出力:
Element : One
Element : XX
Element : X
Num : 17
Num : 0
Num : 0
ここに私の方法:
OIFS=$IFS
IFS='-'
IN="One-XX-X-17.0.0"
ADDR=($IN)
for i in "${ADDR[@]}"; do
echo "Element:$i"
done
IFS='.'
array=(${ADDR[3]})
for element in "${array[@]}"
do
echo "Num:$element"
done
期待どおりの結果:
Num:17
Num:0
Num:0