web-dev-qa-db-ja.com

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

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

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

これをやりたいとしましょう:

$ script.sh var1_var2_var3

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

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

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

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

その演算子は、_$IFS_特殊パラメーター(どの文字に分割するかを伝えるために(ただし、スペース、タブ、および改行がそこで特別な扱いを受けることに注意してください))および_-f_オプションを使用して無効にします(_set -f_)または(_set +f_)globパーツを有効にします。

また、_$IFS_のSはもともと(_$IFS_が由来するBourne Shell内) S区切り文字、POSIXシェルでは、_$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_'
_

それはそれを_var1_と_var2_のみに分割します(余分な空の要素はありません)。

したがって、それを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
_

(空の_$string_を1(not)要素に分割することに注意してください、JavaScriptのsplit()など)。

特別な扱いのタブ、スペース、改行の受け取りを確認するには、以下を比較してください。

_IFS=' '; string=' var1  var2  '
_

(_var1_と_var2_を取得する場所)

_IFS='_'; string='_var1__var2__'
_

取得場所:_''_、_var1_、_''_、_var2_、_''_。

zshまたはshエミュレーションでない限り、kshシェルはそのようなsplit + glob演算子を暗黙的に呼び出さないことに注意してください。そこで、明示的に呼び出す必要があります。 _$=string_はスプリットパーツ、_$~string_はグロブパーツ(_$=~string_は両方)のほか、セパレーターを指定できるスプリット演算子もあります。

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

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

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

そこにs分割用であり、区切り用ではない(また、既知の_$IFS_と一緒に) POSIX非準拠zsh)。 JavaScriptのsplit()とは異なり、空の文字列は0(1ではない)要素に分割されます。

_$IFS_分割との顕著な違いは、${(s:abc:)string}abc文字列で分割されるのに対し、_IFS=abc_はaで分割されることです。 bまたはc

zshおよび_ksh93_を使用すると、スペース、タブ、または改行が受け取る特別な扱いを、_$IFS_で2倍にすることによって削除できます。

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

正規表現での分割

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

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_モジュール内)のサポートが組み込まれていますが、これを使用して文字列を分割することは可能ですが、比較的面倒です。

24

はい、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名前

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

[〜#〜] ifs [〜#〜]

拡張後のワード分割に使用され、組み込みコマンドreadで行をワードに分割するために使用される内部フィールドセパレータ。デフォルト値は「」です。

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

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