Как разбить одну строку на несколько строк, разделенных хотя бы одним пробелом в оболочке bash?
У меня есть строка, содержащая много слов, по крайней мере, один пробел между каждыми двумя. Как я могу разбить строку на отдельные слова, чтобы я мог проходить через них?
Строка передается в качестве аргумента. Например ${2} == "cat cat file"
, Как я могу пройти через это?
Кроме того, как я могу проверить, содержит ли строка пробелы?
11 ответов
Вы пытались просто передать строковую переменную 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
Вероятно, самый простой и безопасный способ в 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]
КСТАТИ:
Если вы не привыкли
$'ANSI-ESCAPED-STRING'
привыкнуть к этому, это экономит время.Если вы не включите
-r
(как вread -a arr <<<"$var"
) затем прочитайте экранирование. Это оставлено как упражнение для читателя.
По второму вопросу:
Чтобы проверить что-то в строке, я обычно придерживаюсь case
, так как это может проверять сразу несколько случаев (примечание: case выполняет только первое совпадение, если вам нужно упасть, используйте multiplce 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% всех потребностей.
Просто используйте встроенные оболочки "set". Например,
установить $ текст
После этого отдельные слова в $ text будут в $1, $2, $3 и т. Д. Для устойчивости обычно делают
set - мусорный текст сдвиг
обрабатывать случай, когда $ text пуст или начинаться с тире. Например:
text = "Это тест" set - мусорный текст сдвиг для слова; делать echo "[$word]" сделанный
Это печатает
[Это] [является] [А] [тестовое задание]
$ 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
echo $WORDS | xargs -n1 echo
Это выводит каждое слово, вы можете впоследствии обработать этот список по своему усмотрению.
(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
Как видите, вы можете использовать одинарные или двойные кавычки без проблем
Заметки:
- это в основном то же самое, что и ответ моба, но таким образом вы сохраняете массив для дальнейших нужд. Если вам нужен только один цикл, вы можете использовать его ответ, который на одну строку короче:)
- пожалуйста, обратитесь к этому вопросу для альтернативных методов разделения строки на основе разделителя.
(B) Чтобы проверить наличие символа в строке, вы также можете использовать совпадение регулярного выражения.
Пример для проверки наличия пробела вы можете использовать:
regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
then
echo "Space here!";
fi
Для проверки пробелов только с помощью bash:
[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"
Для моего варианта использования лучшим вариантом был:
grep -oP '\w+' file
По сути, это регулярное выражение, которое соответствует непрерывным непробельным символам. Это означает, что любой тип и любое количество пробелов не будут совпадать. Параметр -o выводит каждое совпадение слов в отдельной строке.
Другой подход к этому (с использованием Perl):
$ echo foo bar baz | perl -nE 'say for split /\s/'
foo
bar
baz