JavaScriptの「split()」のようなものがシェルにありますか?


18

split()JavaScriptで文字列を配列に分割するのは非常に簡単です。

シェルスクリプトはどうですか?

私はこれをやりたいと言います:

$ script.sh var1_var2_var3

ユーザーがそのような文字列var1_var2_var3をscript.shに渡すと、スクリプト内で文字列を次のような配列に変換します

array=( var1 var2 var3 )
for name in ${array[@]}; do
    # some code
done

1
shellを使用して、何をbashすることができますIFS='_' read -a array <<< "${string}"
グウィリー

perlそれもできます。「純粋な」シェルではありませんが、非常に一般的です。
ソブリク

@Sobrique「純粋な」シェルの技術的な定義も知りませんが、node.jsがあります。
エモリー

私は「それはおそらく、デフォルトでは、私のLinuxボックスにインストールされている」と:)特徴点を心配しないでください上の仕事に傾向がある
Sobrique

回答:


24

Bourne / POSIXのようなシェルにはsplit + glob演算子があり、リストコンテキストでパラメーター展開($var$-...)、コマンド置換($(...))、または算術展開($((...)))を引用符で囲まずに呼び出すたびに呼び出されます。

実際、のfor name in ${array[@]}代わりに誤って呼び出したのですfor name in "${array[@]}"。(実際、そのような演算子を誤っ呼び出すと、多くのバグやセキュリティの脆弱性の原因になることに注意してください)。

その演算子を用いて構成され$IFS、特殊なパラメータと、(どのような(しかしそのスペースを用心、タブや改行が特別な治療を受ける)に分割する文字伝えるために)-f無効にするオプションを(set -f)または有効(set +fglobの部分。

また、Sin $IFSは元々(元々のBourneシェルで$IFSSeparatorでしたが、POSIXシェルでは、inの文字は区切り文字またはターミネータ$IFSとして見るべきです(例については以下を参照)。

分割するには_

string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
array=($string) # invoke the split+glob operator

for i in "${array[@]}"; do # loop over the array elements.

separatordelimiterの違いを確認するには、次を試してください。

string='var1_var2_'

それはに分割しないだろうvar1var2だけ(余分な空要素)。

したがって、JavaScriptのようsplit()にするには、追加の手順が必要になります。

string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
temp=${string}_ # add an extra delimiter
array=($temp) # invoke the split+glob operator

(JavaScriptのように、空$string10ではなく)要素に分割することに注意してくださいsplit())。

特別処理タブ、スペースおよび改行受信を表示するには、以下を比較してください。

IFS=' '; string=' var1  var2  '

(ここで、あなたが得るvar1var2して)

IFS='_'; string='_var1__var2__'

あなたが取得する場所:''var1''var2''

注意zshシェルが暗黙的にしない限りでそのようなことスプリット+グロブ演算子呼び出しませんshkshエミュレーションを。そこで、明示的に呼び出す必要があります。$=string分割部分、$~stringグロブ部分($=~string両方)、および区切り記号を指定できる分割演算子もあります。

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

または空の要素を保持するには:

array=("${(@s:_:)string}")

そこことに注意してくださいsするためのものである分割、ない区切り(とも$IFS、既知のPOSIXの非準拠zsh)。JavaScriptとは異なりsplit()、空の文字列が0(1ではなく)要素に分割されます。

$IFS-splittingとの顕著な違いは${(s:abc:)string}abc文字列で分割するのに対し、with IFS=abcではabまたはで分割することcです。

zshksh93、スペース、タブや改行を受けることを特別な治療は、それらを倍増することによって除去することができます$IFS

歴史的なメモとして、Bourneシェル(祖先または最新のPOSIXシェル)は常に空の要素を削除しました。また、デフォルト以外の値である$ @の分割と展開に関連するいくつかのバグがありました$IFS。たとえば、IFS=_; set -f; set -- $@と同等ではありませんIFS=_; set -f; set -- $1 $2 $3...

正規表現での分割

ここでsplit()、正規表現で分割できるJavaScriptに近いものについては、外部ユーティリティに依存する必要があります。

POSIXツールチェストにawkは、拡張正規表現でsplit分割できる演算子があります(これらは、JavaScriptでサポートされているPerlのような正規表現のサブセットです。)

split() {
  awk -v q="'" '
    function quote(s) {
      gsub(q, q "\\" q q, s)
      return q s q
    }
    BEGIN {
      n = split(ARGV[1], a, ARGV[2])
      for (i = 1; i <= n; i++) printf " %s", quote(a[i])
      exit
    }' "$@"
}
string=a__b_+c
eval "array=($(split "$string" '[_+]+'))"

zshシェルは、(その中のPerl互換の正規表現のサポート組み込み有するzsh/pcreモジュール)が、可能が比較的面倒であるが、それは、文字列を分割するために使用して。


タブ、スペース、改行を使用した特別な処理の理由はありますか?
クオンルム

1
@cuonglm、通常、区切り文字が空白の場合は単語で分割しますが、非空白区切り文字の場合(で分割$PATHするなど:)、通常は空の要素を保持します。Bourneシェルでは、すべての文字が特別な扱いを受けてkshいたため、空白のもの(スペース、タブ、改行のみ)が特別に扱われるように変更されました。
ステファンシャゼル

さて、最近追加されたBourneシェルノートは驚きました。また、完了するzshために、文字列に2文字以上が含まれる場合の処理に関するメモを追加する必要があります${(s:string:)var}か?追加された場合、回答を削除できます:)
cuonglm

1
「$ IFSのSは区切り文字ではなく区切り文字であることにも注意してください」とはどういう意味ですか?私は、力学を理解し、それが末尾の区切り文字を無視しますが、そのSために立っている区切りではなく、区切り文字。少なくとも、それは私のbashのマニュアルに書かれていることです。
テルドン

@terdonは、$IFSそれがseparatorであったBourneシェルに由来し、kshは名前を変更せずに動作を変更しました。split+glob(zshまたはpdkshを除き)単純に分割されないことを強調するために言及します。
ステファンシャゼル

7

はい、使用IFSしてに設定し_ます。次にread -a、配列に格納するために使用-rします(バックスラッシュ展開をオフにします)。これはbashに固有のものであることに注意してください。kshとzshには、わずかに異なる構文を持つ同様の機能があり、プレーンなshには配列変数がまったくありません。

$ r="var1_var2_var3"
$ IFS='_' read -r -a array <<< "$r"
$ for name in "${array[@]}"; do echo "+ $name"; done
+ var1
+ var2
+ var3

からman bash

読んだ

-a名前

単語は、0から始まる配列変数anameの連続インデックスに割り当てられます。anameは、新しい値が割り当てられる前に設定解除されます。他の名前引数は無視されます。

IFS

展開後の単語分割、およびread組み込みコマンドで行を単語に分割するために使用される内部フィールド区切り文字。デフォルト値は `` ''です。

read最初の改行で停止することに注意してください。合格-d ''するreadことを避けるため、その場合には、原因に最後に余分な改行が存在します<<<演算子。手動で削除できます:

IFS='_' read -r -d '' -a array <<< "$r"
array[$((${#array[@]}-1))]=${array[$((${#array[@]}-1))]%?}

これは$r、改行文字またはバックスラッシュが含まれていないことを前提としています。また、最近のバージョンのbashシェルでのみ機能することにも注意してください。
ステファンシャゼラス

@StéphaneChazelas良い点。はい、これは文字列の「基本的な」ケースです。残りについては、誰もがあなたの包括的な答えを求めるべきです。バージョンについてはbashread -abashの4、右で導入されましたか?
フェドルキ

1
申し訳ありませんが、<<<ごく最近追加されたと思いbashますが、2.05b(2002)以来存在しているようです。read -aそれよりも古いです。(およびmkshとyash)<<<からzshもサポートさksh93れていますが、read -aが、bash固有です(-Aksh93、yash、zshにあります)。
ステファンシャゼル

@StéphaneChazelasは、これらの変更がいつ発生したかを見つける「簡単な」方法はありますか?リリースファイルを掘り下げないように「簡単」と言います。おそらくそれらをすべて表示するページです。
フェドルキ

1
そのための変更ログを見ます。zshには、3.1.5までの履歴を持つgitリポジトリもあり、そのメーリングリストは変更の追跡にも使用されます。
ステファンシャゼル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.