У меня есть такая строка:
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
Я хочу иметь возможность разделить это следующим образом:
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
Как это сделать? (предпочтительно в одну строку)
решение1
Когда я увидел ответ Дэвида Постилла, я подумал: «Должно быть более простое решение». После некоторых экспериментов я нашел следующие работы:-
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/почему-следует-избегать-этого-в-баше-и-что-следует-использовать-вместо-этого/17529221#17529221
Но вектор для атаки все равно остался. Я очень хочу иметь в bash метод кавычек строк, как в двойных кавычках ("), но без интерпретации содержимого.