Как разделить строку на разделитель в Bash? - PullRequest
1755 голосов
/ 28 мая 2009

Эта строка хранится в переменной:

IN="bla@some.com;john@home.com"

Теперь я хотел бы разделить строки на ; разделитель так, чтобы у меня было:

ADDR1="bla@some.com"
ADDR2="john@home.com"

Мне не обязательно нужны переменные ADDR1 и ADDR2. Если они являются элементами массива, это даже лучше.


После предложений из приведенных ниже ответов я получил следующее:

#!/usr/bin/env bash

IN="bla@some.com;john@home.com"

mails=$(echo $IN | tr ";" "\n")

for addr in $mails
do
    echo "> [$addr]"
done

Выход:

> [bla@some.com]
> [john@home.com]

Было решение, включающее установку Internal_field_separator (IFS) на ;. Я не уверен, что случилось с этим ответом. Как восстановить IFS назад по умолчанию?

RE: IFS решение, я попробовал это, и оно работает, я сохраняю старый IFS и затем восстанавливаю его:

IN="bla@some.com;john@home.com"

OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
    echo "> [$x]"
done

IFS=$OIFS

Кстати, когда я пытался

mails2=($IN)

Я получил только первую строку при печати в цикле, без скобок вокруг $IN это работает.

Ответы [ 34 ]

3 голосов
/ 25 октября 2016
IN="bla@some.com;john@home.com"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
    echo $entry
done

выход

bla@some.com
john@home.com

Система: Ubuntu 12.04.1

2 голосов
/ 24 апреля 2013

Если места нет, то почему не это?

IN="bla@some.com;john@home.com"
arr=(`echo $IN | tr ';' ' '`)

echo ${arr[0]}
echo ${arr[1]}
2 голосов
/ 02 сентября 2013

Две альтернативы bourne-ish, где ни один не требует массивов bash:

Случай 1 : Делайте это красиво и просто: используйте NewLine в качестве разделителя записей ... например.

IN="bla@some.com
john@home.com"

while read i; do
  # process "$i" ... eg.
    echo "[email:$i]"
done <<< "$IN"

Примечание: в этом первом случае ни один подпроцесс не разветвляется для помощи со списком.

Идея: Может быть, стоит использовать NL экстенсивно для внутреннего использования и преобразовывать его в другой RS только при генерации окончательного результата для внешнего использования .

Случай 2 : Использование «;» в качестве разделителя записей ... например.

NL="
" IRS=";" ORS=";"

conv_IRS() {
  exec tr "$1" "$NL"
}

conv_ORS() {
  exec tr "$NL" "$1"
}

IN="bla@some.com;john@home.com"
IN="$(conv_IRS ";" <<< "$IN")"

while read i; do
  # process "$i" ... eg.
    echo -n "[email:$i]$ORS"
done <<< "$IN"

В обоих случаях под-список может быть составлен в цикле постоянным после завершения цикла. Это полезно при работе со списками в памяти, вместо хранения списков в файлах. {Приписка сохраняй спокойствие и продолжай B-)}

2 голосов
/ 08 января 2015

Помимо фантастических ответов, которые уже были предоставлены, если это просто вопрос распечатки данных, вы можете использовать awk:

awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"

Это устанавливает разделитель полей на ;, так что он может циклически проходить по полям с циклом for и печатать соответственно.

Test

$ IN="bla@some.com;john@home.com"
$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"
> [bla@some.com]
> [john@home.com]

С другим входом:

$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "a;b;c   d;e_;f"
> [a]
> [b]
> [c   d]
> [e_]
> [f]
1 голос
/ 30 апреля 2013

Используйте встроенный set для загрузки массива $@:

IN="bla@some.com;john@home.com"
IFS=';'; set $IN; IFS=$' \t\n'

Тогда пусть партия начнется:

echo $#
for a; do echo $a; done
ADDR1=$1 ADDR2=$2
1 голос
/ 04 апреля 2016

Хорошо, ребята!

Вот мой ответ!

DELIMITER_VAL='='

read -d '' F_ABOUT_DISTRO_R <<"EOF"
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"
NAME="Ubuntu"
VERSION="14.04.4 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.4 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
EOF

SPLIT_NOW=$(awk -F$DELIMITER_VAL '{for(i=1;i<=NF;i++){printf "%s\n", $i}}' <<<"${F_ABOUT_DISTRO_R}")
while read -r line; do
   SPLIT+=("$line")
done <<< "$SPLIT_NOW"
for i in "${SPLIT[@]}"; do
    echo "$i"
done

Почему этот подход "лучший" для меня?

По двум причинам:

  1. Вам не нужно бежать разделитель;
  2. У вас не будет проблем с пробелами . Значение будет правильно разделено в массиве!

[] 's

1 голос
/ 10 октября 2014
IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
set -f
oldifs="$IFS"
IFS=';'; arrayIN=($IN)
IFS="$oldifs"
for i in "${arrayIN[@]}"; do
echo "$i"
done
set +f

Выход:

bla@some.com
john@home.com
Charlie Brown <cbrown@acme.com
!"#$%&/()[]{}*? are no problem
simple is beautiful :-)

Объяснение: Простое присваивание с использованием круглых скобок () преобразует разделенный точкой с запятой список в массив при условии, что при этом вы правильно указали IFS. Стандартный цикл FOR обрабатывает отдельные элементы в этом массиве как обычно. Обратите внимание, что список, заданный для переменной IN, должен быть «жестко» заключен в кавычки, то есть с одиночными галочками.

IFS необходимо сохранять и восстанавливать, поскольку Bash не обрабатывает назначение так же, как команда. Альтернативный обходной путь - обернуть назначение внутри функции и вызвать эту функцию с измененным IFS. В этом случае отдельное сохранение / восстановление IFS не требуется. Спасибо за "Бизе" за указание на это.

1 голос
/ 20 февраля 2015

В оболочке Android большинство предложенных методов просто не работают:

$ IFS=':' read -ra ADDR <<<"$PATH"                             
/system/bin/sh: can't create temporary file /sqlite_stmt_journals/mksh.EbNoR10629: No such file or directory

Что работает:

$ for i in ${PATH//:/ }; do echo $i; done
/sbin
/vendor/bin
/system/sbin
/system/bin
/system/xbin

, где // означает глобальную замену.

0 голосов
/ 29 марта 2019

Это будет даже обрабатывать пробелы:

IFS=';' read ADDR1 ADDR2 <<< $(echo ${IN})
0 голосов
/ 15 ноября 2018

ruby ​​- это просто программа, подобная / usr / bin / yes, а bash обычно направляет вывод в другую программу

у меня это работает:

echo $ PATH | ruby -ne 'помещает $ _. split (":")'

...