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


75

私は文字列を持っています:

one_two_three_four_five

私は変数に保存する必要がAtwo、変数にBfour以上の文字列から

回答:


106

cutwith _をフィールド区切り文字として使用し、目的のフィールドを取得します。

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

echoHere文字列の代わりにand pipeを使用することもできます:

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

代替手段はありますか?私は(bshではなく)kshを使用していますが、kshを返します:構文エラー: '<'予期しない
Alex

@Alex編集内容を確認してください。
-heemayl

いい答えです。少し質問があります。変数「$ s」がパスフォルダーの場合はどうなりますか。パスフォルダーをカットしようとすると、次のようになります$ echo $FILE my_user/my_folder/file.csv $ A="$(cut -d'/' -f2 <<<"$FILE")" $ echo $A [file]*
ヘンリーナバロ

1
そして、あなたはちょうどのみシェル組み込みコマンドを使用して、最後のフィールドをしたい場合-あなたはフィールドの番号がわからないときにその位置を特定する必要がなく、あるいは:echo "${s##*_}"
アミットNaidu

19

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

の使用はデフォルトにunset IFS戻りませんIFS。その後、誰かがOldIFS="$IFS"OldIFS内でnull値を持つ場合。また、IFSの以前の値がデフォルトであると想定しています。唯一の正しい解決策はold="$IFS"、IFS = "$ old"で保存して後で復元することです。または...サブシェルを使用します(...)。または、さらに良いことに、私の答えを読んでください。
sorontar

@sorontar unset IFSIFSデフォルト値に復元しませんが、フィールド分割をデフォルトの効果に戻します。はい、それは制限ですが、実際には通常許容されます。サブシェルの問題は、そこからデータを取得する必要があることです。最後に状態を変更しないソリューションを示しreadます。(POSIXシェルで動作しますが、IIRCはヒアreadドキュメントによりサブシェルで実行されるため、Bourneシェルでは動作<<<しません。)as in your answerは、ksh / bash / zshでのみ動作するバリアントです。
ジル

サブシェルに関するattまたは家宝シェルでも問題は見られません。テストされたすべてのシェル(古いボーンを含む)は、メインシェルで正しい値を提供します。
-sorontar

私のパスが次のような場合はどうなりuser/my_folder/[this_is_my_file]*ますか?私は次の手順を実行したときに、私は得ることである[this_is_my_file]*
ヘンリー・ナバロ

@HenryNavarroこの出力は、私の回答のどのコードスニペットにも対応していません。それらのどれも特別な何もしません/
ジル

17

awk答えを見たかったので、ここに一つあります:

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

1
そして、最後のピースが必要な場合-その位置を指定する必要がない場合、またはフィールドの数がわからない場合:awk -F_ '{print $NF}' <<< 'one_two_3_4_five'
Amit Naidu

8

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

 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"

IFSの変更なし、set -f(パス名の展開)の問題なし位置パラメーター( "$ @")の変更なし。


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

string='one_two_three_four_five'

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

echo "$second $fourth"

このソリューション(here-docとの使用により<<<、後続の改行がすべて削除されます。
また、これは「1ライナー」可変コンテンツに合わせて設計されています。
マルチライナーのソリューションは可能ですが、より複雑な構成が必要です。


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で機能させるには、いくつかの変更を行う必要があります。


read OPが長い文字列から76番目と127番目の要素を抽出したくない限り、使用するソリューションは単純です
...-don_crissti

@don_crisstiええ、もちろんそうですが、似たような構成:readarrayそのような状況では使いやすいかもしれません。
sorontar

@don_crisstiまた、配列を持つシェル用の配列ソリューションも追加しました。POSIXシェルの場合、配列がなくても、127個までの要素の位置パラメーターは、いかなる手段でも「単純な」解決策ではありません。
-sorontar

2

zshあなたは(上の文字列を分割でき_配列に):

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

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

print -r ${elements[4]}

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


set -f最初のソリューションに警告を追加することを忘れないでください。... *多分アスタリスク?
sorontar

@sorontar-なぜ私が必要だと思うのset -fですか?read/ を使用していませんIFS。以下のような文字列で私の解決策を試してみてください*_*_*または何...
don_crissti

zsh用ではありませんが、ユーザーはkshソリューションを要求したため、そのシェルで使用しようとする場合があります。警告は、問題を回避するのに役立ちます。
sorontar

1

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

いいえ、悪い悪い答え
Raj Kumar

0

別の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 = `echo $ {this_str} | awk -F_ '{print $ 1}' `
B =` echo $ {this_str} | awk -F_ '{print $ 2}' `
C =` echo $ {this_str} | awk -F_ '{print $ 3}' `
...など...

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