Совместимый ответ
К этому такому вопросу в bash уже есть много разных способов сделать это.
Но в bash есть много специальных функций, так называемых bashism , которые работают хорошо, но не будут работать в любой другой оболочке .
В частности, массивы , ассоциативный массив и замена шаблона являются чистыми bashisms и могут не работать при других оболочки .
На моем Debian GNU / Linux есть стандартная оболочка с именем dash , но я знаю многих людей, которые любят использовать ksh .
Наконец, в очень маленькой ситуации есть специальный инструмент под названием busybox с собственным интерпретатором оболочки ( ash ).
Запрошенная строка
Пример строки в SO вопросе:
IN="bla@some.com;john@home.com"
Поскольку это может быть полезно с пробелами и пробелами может изменить результат процедуры, я предпочитаю использовать следующую строку:
IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
Разделить строку на основе разделителя в bash (версия> = 4.2)
При pure bash мы можем использовать массивы и IFS :
var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
oIFS="$IFS"
IFS=";"
declare -a fields=($var)
IFS="$oIFS"
unset oIFS
IFS=\; read -a fields <<<"$IN"
Использование этого синтаксиса в недавнем bash не изменяет $IFS
для текущего сеанса, но только для текущей команды:
set | grep ^IFS=
IFS=$' \t\n'
Теперь строка var
разбивается и сохраняется в массив (с именем fields
):
set | grep ^fields=\\\|^var=
fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
var='bla@some.com;john@home.com;Full Name <fulnam@other.org>'
Мы можем запросить переменное содержимое с помощью declare -p
:
declare -p IN fields
declare -- IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
declare -a fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
read
- это самый быстрый способ сделать разделение, потому что нет вилок и не называются внешние ресурсы.
Оттуда вы можете использовать синтаксис, который вы уже знаете, для обработки каждого поля:
for x in "${fields[@]}";do
echo "> [$x]"
done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]
или отбросьте каждое поле после обработки (мне нравится этот смещенный подход):
while [ "$fields" ] ;do
echo "> [$fields]"
fields=("${fields[@]:1}")
done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]
или даже для простой распечатки (более короткий синтаксис):
printf "> [%s]\n" "${fields[@]}"
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]
Обновление: последние bash > = 4.4
Вы можете играть с mapfile
:
mapfile -td \; fields < <(printf "%s\0" "$IN")
Этот синтаксис сохраняет специальные символы, новые строки и пустые поля!
Если вам не нужны пустые поля, вы можете:
mapfile -td \; fields <<<"$IN"
fields=("${fields[@]%$'\n'}") # drop '\n' added by '<<<'
Но вы можете использовать поля через функцию:
myPubliMail() {
printf "Seq: %6d: Sending mail to '%s'..." $1 "$2"
# mail -s "This is not a spam..." "$2" </path/to/body
printf "\e[3D, done.\n"
}
mapfile < <(printf "%s\0" "$IN") -td \; -c 1 -C myPubliMail
(Примечание: \0
в конце строки формата бесполезны, в то время как вам не нужны пустые поля в конце строки)
mapfile < <(echo -n "$IN") -td \; -c 1 -C myPubliMail
Будет что-то вроде:
Seq: 0: Sending mail to 'bla@some.com', done.
Seq: 1: Sending mail to 'john@home.com', done.
Seq: 2: Sending mail to 'Full Name <fulnam@other.org>', done.
Или Удалить новую строку, добавленную <<<
Синтаксис bash в функции:
myPubliMail() {
local seq=$1 dest="${2%$'\n'}"
printf "Seq: %6d: Sending mail to '%s'..." $seq "$dest"
# mail -s "This is not a spam..." "$dest" </path/to/body
printf "\e[3D, done.\n"
}
mapfile <<<"$IN" -td \; -c 1 -C myPubliMail
Будет отображать тот же результат:
Seq: 0: Sending mail to 'bla@some.com', done.
Seq: 1: Sending mail to 'john@home.com', done.
Seq: 2: Sending mail to 'Full Name <fulnam@other.org>', done.
Разделенная строка на основе разделителя в shell
Но если бы вы написали что-то пригодное для использования под многими оболочками, вы должны не использовать bashisms .
Существует синтаксис, используемый во многих оболочках, для разделения строки на первый или последний вхождение подстроки:
${var#*SubStr} # will drop begin of string up to first occur of `SubStr`
${var##*SubStr} # will drop begin of string up to last occur of `SubStr`
${var%SubStr*} # will drop part of string from last occur of `SubStr` to the end
${var%%SubStr*} # will drop part of string from first occur of `SubStr` to the end
(Отсутствие этого - главная причина моей публикации ответа;)
Как указано Score_Under :
#
и %
удаляют самую короткую подходящую строку и
##
и %%
удаляют самое длинное из возможных.
, где #
и ##
означают слева (начало) строки и
%
и %%
означает справа (конец) строки.
Этот небольшой пример скрипта хорошо работает под bash , dash , ksh , busybox и также был протестирован в bash Mac-OS :
var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
while [ "$var" ] ;do
iter=${var%%;*}
echo "> [$iter]"
[ "$var" = "$iter" ] && \
var='' || \
var="${var#*;}"
done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]
Веселись!