次のような文字列があります:
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
次のように分割できるようにしたいと思います。
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
どうすればいいでしょうか? (できればワンライナーを使用)
答え1
David Postill の回答を見たとき、「もっと簡単な解決策があるはずだ」と思いました。いくつか実験してみた後、次の方法を見つけました:-
string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo $string
eval 'for word in '$string'; do echo $word; done'
これは、結果の行 (インラインの回答) を実行する前に、eval
行を展開 (引用符を削除して を展開) するため機能します。string
for word in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $word; done
同じ行に展開される代替手段は次のとおりです。
eval "for word in $string; do echo \$word; done"
ここではstring
二重引用符内では が展開されますが、行が実行される前に が展開されない$
ようにエスケープする必要がありますword
(他の形式では、一重引用符を使用しても同じ効果があります)。結果は次のようになります。
[~/]$ string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
[~/]$ echo $string
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
[~/]$ eval 'for word in '$string'; do echo $word; done'
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
[~/]$ eval "for word in $string; do echo \$word; done"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
答え2
最も簡単な解決策は、引用符で囲まれた引数の配列を作成し、必要に応じてループしたり、コマンドに直接渡したりすることです。
eval "array=($string)"
for arg in "${array[@]}"; do echo "$arg"; done
ps より簡単な方法を見つけた場合はコメントしてくださいeval
。
編集:
@Hubbitus の回答を基に、完全にサニタイズされ、適切に引用されたバージョンを作成しました。注: これはやり過ぎで、ほとんどの句読点の前の二重引用符または一重引用符で囲まれたセクションに追加のバックスラッシュが残りますが、攻撃に対しては無敵です。
declare -a "array=($( echo "$string" | sed 's/[][`~!@#$%^&*():;<>.,?/\|{}=+-]/\\&/g' ))"
興味のある読者は、必要に応じて修正してください。http://ideone.com/FUTHhj
答え3
xargs はそれをかなりうまく実行できるようです:
$ a='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
$ printf "%s" "$a" | xargs -n 1 printf "%s\n"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
答え4
declare
の代わりに を使用することもできますeval
。例:
の代わりに:
string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo "Initial string: $string"
eval 'for word in '$string'; do echo $word; done'
する:
declare -a "array=($string)"
for item in "${array[@]}"; do echo "[$item]"; done
ただし、入力がユーザーから行われる場合は、それほど安全ではないことに注意してください。
したがって、次のような文字列で試してみると:
string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'
評価されますhostname
(もちろん のようなものもあるかもしれませんrm -rf /
)!
これを防ぐための非常に単純な試みは、バックトリック ` や $ などの文字を置き換えるだけです。
string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'
declare -a "array=( $(echo $string | tr '`$<>' '????') )"
for item in "${array[@]}"; do echo "[$item]"; done
出力は次のようになります:
[aString that may haveSpaces IN IT]
[bar]
[foo]
[bamboo]
[bam boo]
[?hostname?]
方法と長所と短所に関する詳細は、次の優れた回答で確認できます。https://stackoverflow.com/questions/17529220/why-should-eval-be-avoided-in-bash-and-what-should-i-use-instead/17529221#17529221
しかし、攻撃の余地はまだ残されています。 私は、文字列を二重引用符(")のように引用するbashメソッドを、内容を解釈せずに使用したいと考えています。。