Как разбить строку с кавычками (например, аргументы команды) в Bash?

6348
foxneSs

У меня есть такая строка:

"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" 

Я хочу иметь возможность разделить это так:

aString that may haveSpaces IN IT bar foo bamboo  bam boo 

Как я могу это сделать? (предпочтительно с использованием одной строки)

6
[so] дубликат: [Разбивать строку только по пробелам, которые находятся вне кавычек] (http://stackoverflow.com/q/12821302) DavidPostill 8 лет назад 0
@DavidPostill вопросы совершенно разные на самом деле. foxneSs 8 лет назад 0
Не совсем, это та же самая общая проблема. DavidPostill 8 лет назад 0
@DavidPostill - это гораздо более простая проблема: все, что ему нужно, это `для l в" aString, которая может иметь пробелы в нем "bar foo" bamboo "" bam boo "; сделать эхо $ l; done` AFH 8 лет назад 0
@AFH лол. Я только что опубликовал гораздо более длинный ответ. Единственная разница в выводе состояла в том, что мой сохранил «». Я пропустил тот факт, что ОП не нужны в выводе. DavidPostill 8 лет назад 0
@AFH Вы должны оставить свой комментарий в качестве ответа. DavidPostill 8 лет назад 0
@DavidPostill - это сложнее, если строка находится в переменной. Если строка находится в `$ s`, то` для l в $ s; сделать эхо $ l; done` принимает кавычки как литералы и разделяет пробелы. Мне нужно выйти сейчас, так что не стесняйтесь решать это. AFH 8 лет назад 0
это называется токенизацией строки .. например, современные языки программирования / скриптинга / библиотеки, имеют функцию токенайзера строки. Для bash http://stackoverflow.com/questions/5382712/bash-how-to-tokenize-a-string-variable barlop 8 лет назад 0
@barlop Маркер в связанном вопросе разделяется на все пробелы, а не только на те, которые находятся вне кавычек. DavidPostill 8 лет назад 0

5 ответов на вопрос

3
DavidPostill

How do I do that?

$ for l in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $l; done aString that may haveSpaces IN IT bar foo bamboo bam boo 

What do I do if my string is in a bash variable?

The simple approach of using the bash string tokenizer will not work, as it splits on every space not just the ones outside quotes:

DavidPostill@Hal /f/test $ cat ./test.sh #! /bin/bash string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' for word in $string; do echo "$word"; done DavidPostill@Hal /f/test $ ./test.sh "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" 

To get around this the following shell script (splitstring.sh) shows one approach:

#! /bin/bash string=$(cat <<'EOF' "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" EOF ) echo Source String: "$string" results=() result='' inside='' for (( i=0 ; i<${#string} ; i++ )) ; do char=$ if [[ $inside ]] ; then if [[ $char == \\ ]] ; then if [[ $inside=='"' && $ == '"' ]] ; then let i++ char=$inside fi elif [[ $char == $inside ]] ; then inside='' fi else if [[ $char == ["'"'"'] ]] ; then inside=$char elif [[ $char == ' ' ]] ; then char='' results+=("$result") result='' fi fi result+=$char done if [[ $inside ]] ; then echo Error parsing "$result" exit 1 fi echo "Output strings:" for r in "$" ; do echo "$r" | sed "s/\"//g" done 

Output:

$ ./splitstring.sh Source String: "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" Output strings: aString that may haveSpaces IN IT bar foo bamboo bam boo 

Source: StackOverflow answer Split a string only by spaces that are outside quotes by choroba. Script has been tweaked to match the requirements of the question.

3
Shogan Aversa-Druesne

Самое простое решение - использование массива аргументов в кавычках, который вы можете затем зациклить, если хотите, или передать непосредственно команде.

eval "array=($string)"  for arg in "$"; do echo "$arg"; done  

Пожалуйста, прокомментируйте, если вы найдете более простой способ без eval.

Редактировать:

Основываясь на ответе @Hubbitus, мы имеем полностью очищенную и правильно процитированную версию. Примечание: это избыточно и фактически оставит дополнительные обратные слэши в разделах с двойными или одинарными кавычками, предшествующими большинству знаков препинания, но неуязвимо для атаки.

declare -a "array=($( echo "$string" | sed 's/[][`~!@#$%^&*():;<>.,?/\|{}=+-]/\\&/g' ))" 

Я оставляю заинтересованным читателям возможность изменять по своему усмотрению http://ideone.com/FUTHhj

2
AFH

When I saw David Postill's answer, I thought "there must be a simpler solution". After some experimenting I found the following works:-

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' echo $string eval 'for word in '$string'; do echo $word; done' 

This works because eval expands the line (removing the quotes and expanding string) before executing the resultant line (which is the in-line answer):

for word in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $word; done 

An alternative which expands to the same line is:

eval "for word in $string; do echo \$word; done" 

Here string is expanded within the double-quotes, but the $ must be escaped so that word in not expanded before the line is executed (in the other form the use of single-quotes has the same effect). The results are:-

[~/]$ 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
Hubbitus

Вы можете сделать это 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 "$"; do echo "[$item]"; done 

Но обратите внимание, это не намного безопаснее, если ввод поступает от пользователя!

Итак, если вы попробуете это, скажем, строка вроде:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`' 

Вас hostnameоценивают (там, конечно, может быть что-то вроде rm -rf /)!

Очень-очень простая попытка защитить его, просто замените символы типа backtrick `и $:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`' declare -a "array=( $(echo $string | tr '`$<>' '????') )" for item in "$"; 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- потребительная вместо / 17529221 # 17529221

Но там все же оставлен вектор для атаки. Я очень хочу иметь в bash метод строковых кавычек, как в двойных кавычках ("), но без интерпретации содержимого .

0
tinyhare

использовать awk

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN }' aString that may haveSpaces IN IT bar foo bamboo bam boo 

Или преобразуйте пробел в «% 20» или «_», чтобы его можно было обработать следующей командой throw pip:

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN print }' aString_that_may_haveSpaces_IN_IT bar foo bamboo bam_boo 

ссылка: Awk рассматривает строку в двойных кавычках как один токен и игнорирует пробел между

Похожие вопросы