7

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

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

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

aString that may haveSpaces IN IT
bar
foo
bamboo  
bam boo

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

5 ответов5

4

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

eval "array=($string)"

for arg in "${array[@]}"; do echo "$arg"; done   

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

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

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

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

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

3

Когда я увидел ответ Дэвида Постила, я подумал:"Должно быть более простое решение". После некоторых экспериментов я нашел следующие работы:

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
3

Как я могу это сделать?

$ 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

Что мне делать, если моя строка находится в переменной bash ?

Простой подход с использованием bash строки Tokenizer не будет работать, так как он расщепляется на каждом пространстве не только те внешние кавычки:

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"

Чтобы обойти это, следующий сценарий оболочки (splitstring.sh) показывает один подход:

#! /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=${string:i:1}
    if [[ $inside ]] ; then
        if [[ $char == \\ ]] ; then
            if [[ $inside=='"' && ${string:i+1:1} == '"' ]] ; 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 "${results[@]}" ; do
    echo "$r" | sed "s/\"//g"
done

Выход:

$ ./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

Источник: StackOverflow answer Разбить строку только по пробелам, которые находятся вне кавычек Чороба. Скрипт был изменен в соответствии с требованиями вопроса.

2

Вы можете сделать это с помощью 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 /)!

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

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- потребительная вместо /17529221 # 17529221

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

0

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

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN {FPAT = "([^ ]+)|(\"[^\"]+\")"}{for(i=1;i<=NF;i++){gsub("\"","",$i);print $i} }'
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 {FPAT = "([^ ]+)|(\"[^\"]+\")"}{for(i=1;i<=NF;i++){gsub("\"","",$i);gsub(" ","_",$i)} print }'
aString_that_may_haveSpaces_IN_IT bar foo bamboo bam_boo

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .