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

1584

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

IN="[email protected];[email protected]"

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

ADDR1="[email protected]"
ADDR2="[email protected]"

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


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

#!/usr/bin/env bash

IN="[email protected];[email protected]"

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

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

Вывод:

> [[email protected]]
> [[email protected]]

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

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

IN="[email protected];[email protected]"

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

IFS=$OIFS

Кстати, когда я пробовал

mails2=($IN)

У меня появилась первая строка при печати в цикле, без скобок вокруг $IN она работает.

  • 13
    Что касается вашего «Edit2»: вы можете просто «сбросить IFS», и он вернется в состояние по умолчанию. Нет необходимости сохранять и восстанавливать его явно, если только у вас нет причин ожидать, что для него уже установлено значение, отличное от значения по умолчанию. Более того, если вы делаете это внутри функции (а если нет, то почему бы и нет?), Вы можете установить IFS в качестве локальной переменной, и он вернется к своему предыдущему значению после выхода из функции.
  • 17
    @BrooksMoses: (a) +1 за использование local IFS=... где это возможно; (b) -1 для unset IFS , это точно не сбрасывает IFS к его значению по умолчанию, хотя я считаю, что неустановленный IFS ведет себя так же, как и значение по умолчанию IFS ($ '\ t \ n'), однако это кажется плохим практикуйте слепое предположение, что ваш код никогда не будет вызываться с IFS, установленным в произвольное значение; (c) другая идея состоит в том, чтобы вызвать подоболочку: (IFS=$custom; ...) при выходе из подоболочки IFS вернется к тому, что было изначально.
Показать ещё 5 комментариев
Теги:
scripting

34 ответа

877
Лучший ответ

Вы можете установить переменную internal field separator (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"
  • 17
    Это, наверное, лучший способ. Как долго IFS сохранит свое текущее значение, сможет ли он испортить мой код, будучи установленным, когда его не должно быть, и как я могу сбросить его, когда я закончу с этим?
  • 6
    теперь, после исправления, только в течение действия команды чтения :)
Показать ещё 15 комментариев
781

Взято из Bash shell script split array:

IN="[email protected];[email protected]"
arrIN=(${IN//;/ })

Пояснение:

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

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

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

  • Если исходная строка содержит пробелы, вам нужно будет использовать IFS:
    • IFS=':'; arrIN=($IN); unset IFS;
  • Если исходная строка содержит пробелы, а разделитель - это новая строка, вы можете установить IFS с помощью:
    • IFS=$'\n'; arrIN=($IN); unset IFS;
  • 71
    Я просто хочу добавить: это самый простой из всех, вы можете получить доступ к элементам массива с помощью $ {arrIN [1]} (конечно, начиная с нулей)
  • 24
    Нашел его: техника изменения переменной внутри $ {} известна как «расширение параметров».
Показать ещё 15 комментариев
217

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

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

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

  • 0
    Вы должны были сохранить ответ IFS. Это научило меня чему-то, чего я не знал, и определенно создал массив, тогда как это просто дешевая замена.
  • 0
    Понимаю. Да, я нахожу себя в этих глупых экспериментах, я буду учиться чему-то новому каждый раз, когда пытаюсь что-то ответить. Я отредактировал материал, основанный на обратной связи #bash IRC и восстановленный :)
Показать ещё 4 комментария
115

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

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

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

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

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

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

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

IN="[email protected];[email protected]"

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

 IN="[email protected];[email protected];Full Name <[email protected]>"

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

При чистом bash мы можем использовать массивы и IFS:

var="[email protected];[email protected];Full Name <[email protected]>"

oIFS="$IFS"
IFS=";"
declare -a fields=($var)
IFS="$oIFS"
unset oIFS

IFS=\; read -a fields <<<"$var"

Использование этого синтаксиса в недавнем bash не меняет $IFS для текущего сеанса, но только для текущей команды:

set | grep ^IFS=
IFS=$' \t\n'

Теперь строка var разделяется и сохраняется в массиве (с именем fields):

set | grep ^fields=\\\|^var=
fields=([0]="[email protected]" [1]="[email protected]" [2]="Full Name <[email protected]>")
var='[email protected];[email protected];Full Name <[email protected]>'

Мы можем запросить переменное содержимое с помощью declare -p:

declare -p var fields
declare -- var="[email protected];[email protected];Full Name <[email protected]>"
declare -a fields=([0]="[email protected]" [1]="[email protected]" [2]="Full Name <[email protected]>")

read - самый быстрый способ выполнить разделение, потому что не существует вил и внешних ресурсов.

Оттуда вы можете использовать синтаксис, который вы уже знаете для обработки каждого поля:

for x in "${fields[@]}";do
    echo "> [$x]"
    done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

или отбросить каждое поле после обработки (мне нравится этот подход переключения):

while [ "$fields" ] ;do
    echo "> [$fields]"
    fields=("${fields[@]:1}")
    done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

или даже для простой распечатки (более короткий синтаксис):

printf "> [%s]\n" "${fields[@]}"
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

Разделить строку на основе разделителя в

Но если вы напишете что-нибудь, пригодное для использования во многих оболочках, вы должны использовать не бахизмы.

Существует синтаксис, используемый во многих оболочках, для разделения строки на первое или последнее вхождение подстроки:

${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:

# и % удалите кратчайшую совпадающую строку и

## и %% удалить максимально возможное время.

Этот небольшой образец script работает хорошо под bash, , , и также был протестирован в Mac OS bash:

var="[email protected];[email protected];Full Name <[email protected]>"
while [ "$var" ] ;do
    iter=${var%%;*}
    echo "> [$iter]"
    [ "$var" = "$iter" ] && \
        var='' || \
        var="${var#*;}"
  done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

Удачи!

  • 12
    Подстановки # , ## , % и %% имеют то, что IMO проще запомнить (насколько они удаляются): # и % удаляют самую короткую подходящую строку, а ## и %% удаляют самую длинную из возможных.
  • 1
    IFS=\; read -a fields <<<"$var" завершается с ошибкой на новых строках и добавляет завершающий перевод на новую строку. Другое решение удаляет завершающее пустое поле.
Показать ещё 3 комментария
77

Я видел пару ответов, ссылающихся на команду cut, но все они были удалены. Немного странно, что об этом никто не говорил, потому что я считаю это одной из наиболее полезных команд для этого типа вещей, особенно для разбора файлов журналов с разделителями.

В случае разделения этого конкретного примера на массив bash script tr, вероятно, более эффективен, но cut может быть использован и более эффективен, если вы хотите вытащить определенные поля из средний.

Пример:

$ echo "[email protected];[email protected]" | cut -d ";" -f 1
[email protected]
$ echo "[email protected];[email protected]" | cut -d ";" -f 2
[email protected]

Вы можете, очевидно, поместить это в цикл и перебрать параметр -f, чтобы вытащить каждое поле независимо.

Это становится более полезным, если у вас есть файл журнала с разделителями с такими строками:

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

cut очень удобно, чтобы иметь возможность cat этого файла и выбрать конкретное поле для дальнейшей обработки.

  • 4
    Слава за использование cut , это правильный инструмент для работы! Очищено намного больше, чем любой из этих взломов.
  • 4
    Этот подход будет работать, только если вы заранее знаете количество элементов; вам нужно запрограммировать немного логики вокруг него. Он также запускает внешний инструмент для каждого элемента.
Показать ещё 1 комментарий
76

Как насчет этого подхода:

IN="[email protected];[email protected]" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

Источник

  • 6
    +1 ... но я бы не назвал переменную "Массив" ... пэт пев, наверное. Хорошее решение
  • 14
    +1 ... но "установить" и объявить -a не нужны. Вы могли бы также использовать только IFS";" && Array=($IN)
Показать ещё 3 комментария
62

Это сработало для меня:

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2
  • 1
    Cut работает только с одним символом в качестве разделителя.
  • 0
    Хотя он работает только с одним символом-разделителем, это то, что ищет OP (записи, разделенные точкой с запятой).
61
  • 3
    -1 что если строка содержит пробелы? например IN="this is first line; this is second line" arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) ) создаст массив из 8 элементов в этом case (элемент для каждого слова разделен пробелом), а не 2 (элемент для каждой строки разделен точкой с запятой)
  • 3
    @ Luca Нет, скрипт sed создает ровно две строки. То, что создает несколько записей для вас, это когда вы помещаете их в массив bash (который по умолчанию разделяется на пробелы)
Показать ещё 2 комментария
59

Это также работает:

IN="[email protected];[email protected]"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

Будьте осторожны, это решение не всегда правильно. Если вы передадите только "[email protected]", он назначит его как ADD1, так и ADD2.

  • 1
    Вы можете использовать -s, чтобы избежать упомянутой проблемы: superuser.com/questions/896800/… "-f, --fields = LIST выбрать только эти поля; также вывести любую строку, которая не содержит символа-разделителя, если опция -s не указано»
31

Я думаю, AWK - лучшая и эффективная команда для решения вашей проблемы. AWK включен в Bash по умолчанию почти в каждом дистрибутиве Linux.

echo "[email protected];[email protected]" | awk -F';' '{print $1,$2}'

даст

[email protected] [email protected]

Конечно, вы можете хранить каждый адрес электронной почты, переопределяя поле печати awk.

  • 2
    Или еще проще: echo "[email protected]; [email protected]" | awk 'BEGIN {RS = ";"} {print}'
  • 0
    @Jaro Это отлично сработало, когда у меня была строка с запятыми, и мне нужно было переформатировать ее в строки. Благодарю.
Показать ещё 2 комментария
25

Другой подход к Darron answer, вот как я это делаю:

IN="[email protected];[email protected]"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)
  • 0
    Это не работает
  • 0
    Я думаю, что это так! Запустите приведенные выше команды, а затем «echo $ ADDR1 ... $ ADDR2», и я получу вывод «[email protected] ... [email protected]»
Показать ещё 5 комментариев
23

Это должно работать везде:

echo "luke;yoda;leila" | tr ";" "\n"

(Обратите внимание, что этот метод стоит того, если вы новичок в Bash, и вам просто нужен простой и короткий трюк. Академический и "правильный" способ заключается в использовании IFS, как указано в других сообщениях.)

22

В Bash, пуленепробиваемый способ, который будет работать, даже если ваша переменная содержит символы новой строки:

IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")

Облик:

$ in=$'one;two three;*;there is\na newline\nin this field'
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two three" [2]="*" [3]="there is
a newline
in this field")'

Трюк для этого заключается в использовании опции -d read (разделитель) с пустым разделителем, так что read вынужден читать все, что он кормил. И мы корнем read с точностью до содержимого переменной in, без конечной новой строки благодаря printf. Обратите внимание, что мы также помещаем разделитель в printf, чтобы гарантировать, что строка, переданная в read, имеет трейлинг-разделитель. Без него read обрезает потенциальные конечные пустые поля:

$ in='one;two;three;'    # there an empty field
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two" [2]="three" [3]="")'

сохраняется оставшееся пустое поле.


Обновление для Bash ≥4.4

Так как Bash 4.4, встроенный mapfile (aka readarray) поддерживает параметр -d для указания разделителя. Следовательно, другой канонический способ:

mapfile -d ';' -t array < <(printf '%s;' "$in")
  • 5
    Я нашел его как редкое решение в этом списке, которое корректно работает с \n , пробелами и * одновременно. Также нет петель; Переменная массива доступна в оболочке после выполнения (в отличие от ответа с наибольшим количеством голосов). Обратите внимание, что in=$'...' он не работает с двойными кавычками. Я думаю, что нужно больше голосов.
18

Как насчет этого одного лайнера, если вы не используете массивы:

IFS=';' read ADDR1 ADDR2 <<<$IN
  • 0
    Попробуйте использовать read -r ... чтобы, например, два символа "\ t" во входных данных оказались одинаковыми двумя символами в ваших переменных (вместо одного символа табуляции).
  • 0
    -1 Это не работает здесь (Ubuntu 12.04). Добавление echo "ADDR1 $ADDR1"\n echo "ADDR2 $ADDR2" к вашему фрагменту приведет к выводу ADDR1 [email protected] [email protected]\nADDR2 (\ n - ADDR1 [email protected] [email protected]\nADDR2 )
Показать ещё 2 комментария
16

Без настройки IFS

Если у вас есть только один двоеточие, вы можете это сделать:

a="foo:bar"
b=${a%:*}
c=${a##*:}

вы получите:

b = foo
c = bar
14

Вот чистый 3-лайнер:

in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done

где IFS разграничивает слова на основе разделителя, а () используется для создания array. Затем [@] используется для возврата каждого элемента в виде отдельного слова.

Если после этого у вас есть какой-либо код, вам также необходимо восстановить $IFS, например. unset IFS.

  • 4
    Использование $in кавычках позволяет расширять символы подстановки.
7

Существует простой и понятный способ:

echo "add:sfff" | xargs -d: -i  echo {}

Но вы должны использовать gnu xargs, BSD xargs can not support -d delim. Если вы используете яблочный mac, как я. Вы можете установить gnu xargs:

brew install findutils

затем

echo "add:sfff" | gxargs -d: -i  echo {}
4

Следующая функция Bash/zsh разделяет свой первый аргумент на разделителе, заданном вторым аргументом:

split() {
    local string="$1"
    local delimiter="$2"
    if [ -n "$string" ]; then
        local part
        while read -d "$delimiter" part; do
            echo $part
        done <<< "$string"
        echo $part
    fi
}

Например, команда

$ split 'a;b;c' ';'

дает

a
b
c

Этот вывод может, например, быть передан в другие команды. Пример:

$ split 'a;b;c' ';' | cat -n
1   a
2   b
3   c

По сравнению с другими полученными решениями, это имеет следующие преимущества:

  • IFS не переопределяется: из-за динамического охвата четных локальных переменных переопределение IFS по циклу заставляет новое значение протекать в вызовы функций, выполняемые внутри цикла.

  • Массивы не используются: чтение строки в массив с использованием read требует наличия флага -a в Bash и -a в zsh.

При желании функцию можно поместить в script следующим образом:

#!/usr/bin/env bash

split() {
    # ...
}

split "$@"
  • 0
    работает и аккуратно модульный.
4

Это самый простой способ сделать это.

spo='one;two;three'
OIFS=$IFS
IFS=';'
spo_array=($spo)
IFS=$OIFS
echo ${spo_array[*]}
3

вы можете применить awk для многих ситуаций

echo "[email protected];[email protected]"|awk -F';' '{printf "%s\n%s\n", $1, $2}'

также вы можете использовать этот

echo "[email protected];[email protected]"|awk -F';' '{print $1,$2}' OFS="\n"
3
IN="[email protected];[email protected]"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
    echo $entry
done

Выход

[email protected]
[email protected]

Система: Ubuntu 12.04.1

  • 0
    IFS не устанавливается в конкретном контексте read здесь и, следовательно, он может расстроить остальную часть кода, если таковой имеется.
2

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

IN="[email protected];[email protected]"
arr=(`echo $IN | tr ';' ' '`)

echo ${arr[0]}
echo ${arr[1]}
2

Здесь есть несколько интересных ответов (ошибочный вариант), но для чего-то аналогичного расколу на других языках - вот что я понял в исходном вопросе - я решил:

IN="[email protected];[email protected]"
declare -a a="(${IN/;/ })";

Теперь ${a[0]}, ${a[1]} и т.д., как и следовало ожидать. Используйте ${#a[*]} для количества терминов. Или, конечно же, повторить:

for i in ${a[*]}; do echo $i; done

ВАЖНОЕ ПРИМЕЧАНИЕ:

Это работает в тех случаях, когда нет проблем, о которых можно было бы беспокоиться, что решило мою проблему, но может не решить вашу проблему. Пойдите с решением $IFS в этом случае.

  • 0
    Не работает, если IN содержит более двух адресов электронной почты. Пожалуйста, обратитесь к той же идее (но исправлено) в ответе Палиндрома
  • 0
    Лучше использовать ${IN//;/ } (двойная косая черта), чтобы он также работал с более чем двумя значениями. Помните, что любой подстановочный знак ( *?[ ) Будет раскрыт. И конечное пустое поле будет отброшено.
1

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

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

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

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

Из-за двух причин:

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

[] 's

  • 0
    FYI, /etc/os-release и /etc/lsb-release предназначены для получения, а не для анализа. Так что твой метод действительно неправильный. Более того, вы не совсем отвечаете на вопрос о том, как разбить строку на разделитель.
1

В оболочке 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

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

  • 1
    Сбой, если любая часть $ PATH содержит пробелы (или переводы строки). Также расширяются подстановочные знаки (звездочка *, знак вопроса? И фигурные скобки […]).
1

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

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

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

Test

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

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

$ 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

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

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

IN="[email protected]
[email protected]"

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="[email protected];[email protected]"
IN="$(conv_IRS ";" <<< "$IN")"

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

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

1

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

IN="[email protected];[email protected]"
IFS=';'; set $IN; IFS=$' \t\n'

Затем начнем вечеринку:

echo $#
for a; do echo $a; done
ADDR1=$1 ADDR2=$2
  • 0
    Лучше использовать set -- $IN чтобы избежать некоторых проблем с «$ IN», начинающимся с тире. Тем не менее, расширение без кавычек $IN будет расширять символы подстановки ( *?[ ).
0

Еще один поздний ответ... Если вы настроены на java, вот решение bashj (https://sourceforge.net/projects/bashj/):

#!/usr/bin/bashj

#!java

private static String[] cuts;
private static int cnt=0;
public static void split(String words,String regexp) {cuts=words.split(regexp);}
public static String next() {return(cnt<cuts.length ? cuts[cnt++] : "null");}

#!bash

IN="[email protected];[email protected]"

: j.split($IN,";")    # java method call

while true
do
    NAME=j.next()     # java method call
    if [ $NAME != null ] ; then echo $NAME ; else exit ; fi
done
0

Возможно, это не самое элегантное решение, но работает с * и пробелами:

IN="bla@so me.com;*;[email protected]"
for i in `delims=${IN//[^;]}; seq 1 $((${#delims} + 1))`
do
   echo "> [`echo $IN | cut -d';' -f$i`]"
done

Выходы

> [bla@so me.com]
> [*]
> [[email protected]]

Другой пример (разделители в начале и в конце):

IN=";bla@so me.com;*;[email protected];"
> []
> [bla@so me.com]
> [*]
> [[email protected]]
> []

В основном он удаляет каждый символ, отличный от ;, делая delims, например. ;;;. Затем он выполняет цикл for от 1 до number-of-delimiters, как подсчитано ${#delims}. Последним шагом является безопасное получение $i -й части с помощью cut.

0
IN='[email protected];[email protected];Charlie Brown <[email protected];!"#$%&/()[]{}*? 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

Вывод:

[email protected]
[email protected]
Charlie Brown <[email protected]
!"#$%&/()[]{}*? are no problem
simple is beautiful :-)

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

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

  • 0
    !"#$%&/()[]{}*? are no problem ну ... не совсем: []*? являются символами глобуса. Так что насчет создания этого каталога и файла:` mkdir '! "# $% & '; коснитесь '! "# $% & / () [] {} получил хахахаха - не проблема» и запустил команду? простой может быть прекрасным, но когда он сломан, он сломан.
  • 0
    @gniourf_gniourf Строка хранится в переменной. Пожалуйста, смотрите оригинальный вопрос.
Показать ещё 4 комментария
0

Однострочный разделитель строки, разделенной символом ';' в массив:

IN="[email protected];[email protected]"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}

Это только устанавливает IFS в подоболочку, поэтому вам не нужно беспокоиться о сохранении и восстановлении его значения.

  • 0
    -1 это не работает здесь (Ubuntu 12.04). он печатает только первый эхо со всеми значениями $ IN, в то время как второй пустой. Вы можете увидеть это, если поставить echo "0:" $ {ADDRS [0]} \ n echo "1:" $ {ADDRS [1]}, вывод 0: [email protected];[email protected]\n 1: (\ n - новая строка)
  • 1
    пожалуйста, обратитесь к ответу nickjb для рабочей альтернативы этой идее stackoverflow.com/a/6583589/1032370
Показать ещё 1 комментарий
-1

Вы также можете:

dirList=(
some
list
of
elements
)

for i in ${dirList[@]}; do
...
done
-7

Существует два простых метода:

cat "text1;text2;text3" | tr " " "\n"

и

cat "text1;text2;text3" | sed -e 's/ /\n/g'
  • 2
    s / cat / echo / g charlimit
  • 2
    -1 Ошибка: cat: text1; text2; text3: нет такого файла или каталога
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню