web-dev-qa-db-ja.com

1つの文字列をbashシェルで少なくとも1つのスペースで区切られた複数の文字列に分割する方法

各単語の間に少なくとも1つのスペースを入れた多数の単語を含む文字列があります。文字列を個々の単語に分割してそれらをループできるようにするにはどうすればよいですか。

文字列は引数として渡されます。例えば。 ${2} == "cat cat file"。どうやってそれをループすることができますか?

また、文字列にスペースが含まれているかどうかを確認する方法はありますか。

194
derrdji

文字列変数をforループに渡すだけでしたか?たとえば、bashは自動的に空白に分割されます。

sentence="This is   a sentence."
for Word in $sentence
do
    echo $Word
done

This
is
a
sentence.
244
mob

個々の要素にアクセスできるように、配列への変換が好きです。

sentence="this is a story"
stringarray=($sentence)

個々の要素に直接アクセスできるようになりました(0から始まります)。

echo ${stringarray[0]}

またはループするために文字列に変換し直す:

for i in "${stringarray[@]}"
do
  :
  # do whatever on $i
done

もちろん、文字列を直接ループすることは以前に答えられました、しかしその答えは後で使うために個々の要素を追跡しないという不利な点がありました:

for i in $sentence
do
  :
  # do whatever on $i
done

Bash配列リファレンス もご覧ください。

259
Highwind

シェルの "set"ビルトインを使うだけです。例えば、

 $ textを設定します

その後、$ text内の個々の単語は、$ 1、$ 2、$ 3などになります。

 set  - ジャンク$ text 
 shift 

$ textが空の場合やダッシュで始まる場合を処理します。例えば:

 text = "これはテストです" 
 set  -  junk $ text 
 shift [Word]。 do 
 echo "[$ Word]" 
 done 

この版画

[これはテストです]
80
Idelic

BASH 3以降でおそらく最も簡単で安全な方法は次のとおりです。

var="string    to  split"
read -ra arr <<<"$var"

(ここでarrname__は文字列の分割された部分を受け取る配列です)、または入力に改行があり、最初の行以上のものが必要な場合:

var="string    to  split"
read -ra arr -d '' <<<"$var"

-d ''のスペースに注意してください、それは残しておくことはできません)、しかしこれは<<<"$var"から予期しない改行を与えるかもしれません(これは暗黙的に末尾にLFを追加するため) 。

例:

touch NOPE
var="* a  *"
read -ra arr <<<"$var"
for a in "${arr[@]}"; do echo "[$a]"; done

期待どおりに出力する

[*]
[a]
[*]

このソリューションは(ここでの以前のすべてのソリューションとは対照的に)予期しない、しばしば制御不能なシェルグロビングの傾向がありません。

また、これはおそらくあなたが望むようにIFSのフルパワーを提供します:

例:

IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd)
for a in "${arr[@]}"; do echo "[$a]"; done

次のようなものを出力します。

[tino]
[x]
[1000]
[1000]
[Valentin Hilbig]
[/home/tino]
[/bin/bash]

ご覧のとおり、この方法でもスペースを保持できます。

IFS=: read -ra arr <<<' split  :   this    '
for a in "${arr[@]}"; do echo "[$a]"; done

出力

[ split  ]
[   this    ]

BASHでのIFSname__の処理はそれ自体が主題であることに注意してください。テストも同様です。

  • unset IFS:SPC、TAB、NLの実行を無視し、オンラインの開始と終了を無視します
  • IFS='':フィールド分離なし、すべてを読み取る
  • IFS=' ':SPCの実行(およびSPCのみ)

最後の例

var=$'\n\nthis is\n\n\na test\n\n'
IFS=$'\n' read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done

出力

1 [this is]
2 [a test]

ながら

unset IFS
var=$'\n\nthis is\n\n\na test\n\n'
read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done

出力

1 [this]
2 [is]
3 [a]
4 [test]

ところで:

  • $'ANSI-ESCAPED-STRING'に慣れていない場合は、時間の節約になります。

  • -rread -a arr <<<"$var"のように)を含めない場合、readはバックスラッシュエスケープを行います。これは、読者の練習問題として残されています。


2番目の質問:

文字列内の何かをテストするために、私は通常casename__に固執します。これは複数のケースを一度にチェックできるためです(注:フォールスルーが必要な場合、caseは最初の一致のみを実行し、マルチプルcasename__ステートメントを使用します) (しゃれ意図):

case "$var" in
'')                empty_var;;                # variable is empty
*' '*)             have_space "$var";;        # have SPC
*[[:space:]]*)     have_whitespace "$var";;   # have whitespaces like TAB
*[^-+.,A-Za-z0-9]*) have_nonalnum "$var";;    # non-alphanum-chars found
*[-+.,]*)          have_punctuation "$var";;  # some punctuation chars found
*)                 default_case "$var";;      # if all above does not match
esac

したがって、次のように戻り値を設定してSPCを確認できます。

case "$var" in (*' '*) true;; (*) false;; esac

なぜcasename__ですか?通常、正規表現シーケンスよりも少し読みやすいため、シェルメタキャラクターのおかげで、すべてのニーズの99%を非常にうまく処理します。

58
Tino
$ echo "This is   a sentence." | tr -s " " "\012"
This
is
a
sentence.

スペースをチェックするには、grepを使用します。

$ echo "This is   a sentence." | grep " " > /dev/null
$ echo $?
0
$ echo "Thisisasentence." | grep " " > /dev/null     
$ echo $?
1
36
DVK

(A)文を単語に分割する(スペース区切り)には、次のように単純にデフォルトのIFSを使用します。

array=( $string )


次のスニペットを実行する例

#!/bin/bash

sentence="this is the \"sentence\"   'you' want to split"
words=( $sentence )

len="${#words[@]}"
echo "words counted: $len"

printf "%s\n" "${words[@]}" ## print array

出力します

words counted: 8
this
is
the
"sentence"
'you'
want
to
split

ご覧のとおり、一重引用符または二重引用符も問題なく使用できます。

注:
- これは基本的に mob の回答と同じですが、このようにして、必要に応じて配列を保存します。単一のループだけが必要な場合は、彼の答えを使用できます。これは1行短くなります。
- 区切り文字に基づいて文字列を分割する別の方法については、 この質問 を参照してください。


(B)文字列内の文字をチェックするには、正規表現の一致を使用することもできます。
あなたが使用できるスペース文字の存在をチェックする例:

regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
    then
        echo "Space here!";
fi
14
Luca Borrione

Bashだけでスペースをチェックするには:

[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"
5
glenn jackman