Ответ 1
Вы пытались просто передать строковую переменную в цикл for
? Bash, для одного, будет автоматически разбиваться на пробелы.
sentence="This is a sentence."
for word in $sentence
do
echo $word
done
This
is
a
sentence.
У меня есть строка, содержащая много слов с хотя бы одним пробелом между двумя. Как я могу разбить строку на отдельные слова, чтобы я мог их пропустить?
Строка передается как аргумент. Например. ${2} == "cat cat file"
. Как я могу пройти через него?
Также, как я могу проверить, содержит ли строка пробелы?
Вы пытались просто передать строковую переменную в цикл for
? Bash, для одного, будет автоматически разбиваться на пробелы.
sentence="This is a sentence."
for word in $sentence
do
echo $word
done
This
is
a
sentence.
Мне нравится преобразование в массив, чтобы иметь возможность доступа к отдельным элементам:
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 Array Reference.
Просто используйте встроенные оболочки "set". Например,
set $text
После этого отдельные слова в $text будут в $1, $2, $3 и т.д. Для надежности, обычно
set -- junk $text shift
для обработки случая, когда $text пуст или начинается с тире. Например:
text="This is a test" set -- junk $text shift for word; do echo "[$word]" done
Отпечатает
[This] [is] [a] [test]
Вероятно, самый простой и безопасный способ в BASH 3 и выше:
var="string to split"
read -ra arr <<<"$var"
(где arr
- это массив, который принимает разделенные части строки) или, если на входе могут быть новые строки, и вы хотите больше, чем только первую строку:
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 ]
Обратите внимание, что обработка IFS
в BASH является предметом для себя, так что ваши тесты, некоторые интересные темы по этому поводу:
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]
BTW:
Если вы не привыкли к $'ANSI-ESCAPED-STRING'
привыкнуть к нему, это время.
Если вы не включаете -r
(например, в read -a arr <<<"$var"
), тогда чтение выполняет обратную косую черту. Это остается как упражнение для читателя.
Во второй вопрос:
Чтобы проверить что-то в строке, я обычно придерживаюсь case
, так как это может проверять сразу несколько случаев (примечание: case только выполняет первое совпадение, если вам нужно использовать инструкции multipull case
), и эта потребность нередко имеет место (каламбур):
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
Почему case
? Поскольку это обычно более читаемо, чем регулярные выражения, и благодаря метасимволам Shell он очень хорошо справляется с 99% всех потребностей.
$ 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
(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, но таким образом вы храните массив для дальнейшего использования. Если вам нужен только один цикл, вы можете использовать его ответ, который на одну строку короче:)
- см. этот вопрос для альтернативных методов для разделения строки на основе разделителя.
(B) Чтобы проверить символ в строке, вы также можете использовать регулярное выражение.
Пример проверки наличия символа пробела, который вы можете использовать:
regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
then
echo "Space here!";
fi
Для проверки пробелов просто с bash:
[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"