web-dev-qa-db-ja.com

区切り文字で文字列を分割し、N番目の要素を取得する

文字列があります:

one_two_three_four_five

上記の文字列から変数A value twoと変数B value fourに保存する必要があります

96
Alex

フィールド区切り文字としてcut_とともに使用して、必要なフィールドを取得します。

A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"

ヒア文字列の代わりにechoとパイプを使用することもできます:

A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"

例:

$ s='one_two_three_four_five'

$ A="$(cut -d'_' -f2 <<<"$s")"
$ echo "$A"
two

$ B="$(cut -d'_' -f4 <<<"$s")"
$ echo "$B"
four
144
heemayl

POSIX shコンストラクトのみを使用すると、 パラメーター置換コンストラクト を使用して、一度に1つの区切り文字を解析できます。このコードは、必要な数のフィールドがあることを前提としています。それ以外の場合は、最後のフィールドが繰り返されます。

string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"

または、引用符で囲まれていないパラメーター置換を ワイルドカード展開 無効にして、 IFSを区切り文字に設定 で使用できます(これは、区切り文字が単一の非-空白文字、または空白シーケンスが区切り文字の場合)。

string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS

これは、位置パラメータを破棄します。関数でこれを行うと、関数の位置パラメータのみが影響を受けます。

さらに別のアプローチは、readビルトインを使用することです。

IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF

awkの解答を見たいと思ったので、ここに1つあります。

A=$(awk -F_ '{print $2}' <<< 'one_two_three_four_five')
B=$(awk -F_ '{print $4}' <<< 'one_two_three_four_five')
21
Paul Evans

最も単純な方法(<<<のシェルの場合)は次のとおりです。

 IFS='_' read -r a second a fourth a <<<"$string"

1つのシェルが不平を言うため、$aの代わりに一時変数$_を使用します。

完全なスクリプトでは:

 string='one_two_three_four_five'
 IFS='_' read -r a second a fourth a <<<"$string"
 echo "$second $fourth"

set -f(パス名展開)の問題ではなく、IFSの変更はありません。位置パラメータ( "$ @")への変更はありません。


IFSやset -fを変更せずにallシェル(そう、すべてのPOSIXが含まれています)に移植可能なソリューションの場合は、同等の(少し複雑な)ヒアドキュメントを使用します。

string='one_two_three_four_five'

IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_

echo "$second $fourth"

この解決策(ヒアドキュメントと<<<の使用の両方)が後続の改行をすべて削除することを理解してください。
そして、これは「ワンライナー」可変コンテンツに設計されています。
マルチライナーのソリューションは可能ですが、より複雑な構成が必要です。


Bashバージョン4.4では非常にシンプルなソリューションが可能です

readarray -d _ -t arr <<<"$string"

echo "array ${arr[1]} ${arr[3]}"   # array numbers are zero based.

多くのPOSIXシェルには配列がないため、POSIXシェルに相当するものはありません。

配列を持つシェルの場合、次のように単純な場合があります。
(attsh、lksh、mksh、ksh、およびbashで動作することがテスト済み)

set -f; IFS=_; arr=($string)

ただし、変数とオプションを保持およびリセットするための追加の配管がたくさんあります。

string='one_* *_three_four_five'

case $- in
    *f*) noglobset=true; ;;
    *) noglobset=false;;
esac

oldIFS="$IFS"

set -f; IFS=_; arr=($string)

if $noglobset; then set -f; else set +f; fi

echo "two=${arr[1]} four=${arr[3]}"

Zshでは、配列は1から始まり、デフォルトでは文字列を分割しません。
これをzshで機能させるには、いくつかの変更を行う必要があります。

11
Isaac

zshを使用すると、文字列を分割できます(_)を配列に:

elements=(${(s:_:)string})

そして、配列インデックスを介して各/任意の要素にアクセスします:

print -r ${elements[4]}

zshでは(ksh/bashとは異なり) 配列のインデックスは1から始まる であることに注意してください。

3
don_crissti

pythonソリューションは許可されますか?

# python -c "import sys; print sys.argv[1].split('_')[1]" one_two_three_four_five
two

# python -c "import sys; print sys.argv[1].split('_')[3]" one_two_three_four_five
four
2
fhgd

別のawkの例;理解しやすい

A=\`echo one_two_three_four_five | awk -F_ '{print $1}'\`  
B=\`echo one_two_three_four_five | awk -F_ '{print $2}'\`  
C=\`echo one_two_three_four_five | awk -F_ '{print $3}'\`  
... and so on...  

変数でも使用できます。
仮定:
this_str = "one_two_three_four_five"
その後、次のように機能します:
A = `エコー$ {this_str} | awk -F_ '{print $ 1}' `
B = `エコー$ {this_str} | awk -F_ '{print $ 2}' `
C = `エコー$ {this_str} | awk -F_ '{print $ 3}' `
... 等々...

1
user274900