Вопрос: Как разбить строку на разделителе в Bash?


У меня есть эта строка, хранящаяся в переменной:

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оно работает.


1486


источник


Ответы:


Вы можете установить внутренний разделитель полей (IFS), а затем пусть он анализирует массив. Когда это происходит в команде, тогда назначение IFSпроисходит только в среде одной команды (для read). Затем он анализирует ввод в соответствии с IFSпеременное значение в массив, который мы затем можем перебрать.

IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
    # process "$i"
done

Он будет анализировать одну строку элементов, разделенных ;, нажимая его в массив. Материал для обработки всего $IN, каждый раз, когда одна строка ввода разделяется ;:

 while IFS=';' read -ra ADDR; do
      for i in "${ADDR[@]}"; do
          # process "$i"
      done
 done <<< "$IN"

904



Взято из Сплит-скрипт оболочки Bash :

IN="bla@some.com;john@home.com"
arrIN=(${IN//;/ })

Объяснение:

Эта конструкция заменяет все вхождения ';'(начальный //означает глобальную замену) в строке INс ' '(одно пробел), а затем интерпретирует строку с разделителями в виде массива (это то, что делают окружающие круглые скобки).

Синтаксис, используемый внутри фигурных скобок для замены каждого ';'характер с ' 'символ называется Расширение параметров ,

Есть некоторые распространенные ошибки:

  1. Если исходная строка имеет пробелы, вам нужно будет использовать IFS :
    • IFS=':'; arrIN=($IN); unset IFS;
  2. Если исходная строка имеет пробелы а также разделитель - это новая строка, вы можете установить IFS с:
    • IFS=$'\n'; arrIN=($IN); unset IFS;

727



Если вы не возражаете обрабатывать их немедленно, мне нравится делать это:

for i in $(echo $IN | tr ";" "\n")
do
  # process
done

Вы можете использовать этот тип цикла для инициализации массива, но, вероятно, есть более простой способ сделать это. Надеюсь, что это поможет.


207



Совместимый ответ

К этому вопросу SO уже существует много другого способа сделать это в , Но у bash есть много особый функций, так называемых bashism это хорошо работает, но это не сработает ни в одном другом ,

В частности, массивы , ассоциативный массив , а также замена шаблонов чисты bashisms и может не работать под другим ракушки ,

На моем Debian GNU / Linux , Eсть стандарт оболочка называется , но я знаю многих людей, которые любят использовать ,

Наконец, в очень маленькой ситуации есть специальный инструмент, называемый с его собственным интерпретатором оболочки ( ).

Запрошенная строка

Образец строки в вопросе SO:

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

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

 IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

Разделить строку на основе разделителя в (версия> = 4.2)

Под чистый 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


117



How about this approach:

IN="bla@some.com;john@home.com" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

Source


80



I've seen a couple of answers referencing the cut command, but they've all been deleted. It's a little odd that nobody has elaborated on that, because I think it's one of the more useful commands for doing this type of thing, especially for parsing delimited log files.

In the case of splitting this specific example into a bash script array, tr is probably more efficient, but cut can be used, and is more effective if you want to pull specific fields from the middle.

Example:

$ echo "bla@some.com;john@home.com" | cut -d ";" -f 1
bla@some.com
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 2
john@home.com

You can obviously put that into a loop, and iterate the -f parameter to pull each field independently.

This gets more useful when you have a delimited log file with rows like this:

2015-04-27|12345|some action|an attribute|meta data

cut is very handy to be able to cat this file and select a particular field for further processing.


71



This worked for me:

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2

62



echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com

58



This also works:

IN="bla@some.com;john@home.com"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

Be careful, this solution is not always correct. In case you pass "bla@some.com" only, it will assign it to both ADD1 and ADD2.


56