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

Есть недостатки как в подходах cookie, так и в не-cookie. Но если вы можете простить недостатки подхода к cookie, вот идея.

Если вы уже используете Google Analytics на своем сайте, вам не нужно писать код, чтобы отслеживать уникальных пользователей самостоятельно. Google Analytics делает это для вас через [cookie] __utma, как описано в документации Google . И повторно используя это значение, вы не создаете дополнительную полезную нагрузку cookie, которая имеет преимущества эффективности при запросах страниц.

И вы могли бы написать код достаточно легко, чтобы получить доступ к этому значению, или использовать этот скрипт getUniqueId().

1786
задан codeforester 22 October 2018 в 21:20
поделиться

18 ответов

Вы можете установить переменную внутреннего разделителя полей (IFS), а затем позволить ей преобразовать ее в массив. Когда это происходит в команде, то присвоение IFS происходит только в среде этой единственной команды (для читать ). Затем он анализирует ввод в соответствии со значением переменной 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"
1148
ответ дан 22 November 2019 в 20:06
поделиться

Две альтернативы 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
ответ дан NevilleDNZ 22 October 2018 в 21:20
поделиться

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

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

Затем, пусть партия начинается:

echo $#
for a; do echo $a; done
ADDR1=$1 ADDR2=$2
\t\n'

Затем, пусть партия начинается:

echo $#
for a; do echo $a; done
ADDR1=$1 ADDR2=$2
1
ответ дан jeberle 22 October 2018 в 21:20
поделиться

Здесь есть несколько интересных ответов (errator esp.), Но для чего-то аналогичного разделению на других языках - что я и имел в виду в первоначальном вопросе - я остановился на этом:

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

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

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

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

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

4
ответ дан Benjamin W. 22 October 2018 в 21:20
поделиться

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

IN="bla@some.com;john@home.com"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}

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

0
ответ дан Peter Mortensen 22 October 2018 в 21:20
поделиться

Ладно, ребята!

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

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
ответ дан Eduardo Lucio 22 October 2018 в 21:20
поделиться

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

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

Источник

84
ответ дан BLeB 22 October 2018 в 21:20
поделиться

Еще один поздний ответ ... Если вы склонны к 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="bla@some.com;john@home.com"

: 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
-5
ответ дан Fil 22 October 2018 в 21:20
поделиться

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

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2
100
ответ дан lfender6445 22 October 2018 в 21:20
поделиться

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

IFS=';' read ADDR1 ADDR2 <<<$IN
21
ответ дан Darron 22 October 2018 в 21:20
поделиться

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

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

Пример:

$ 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

Очевидно, что вы можете поместить это в цикл и выполнить итерацию параметра -f для независимого извлечения каждого поля.

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

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

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

136
ответ дан DougW 22 October 2018 в 21:20
поделиться

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

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

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

вы получите:

b = foo
c = bar
16
ответ дан Emilien Brigand 22 October 2018 в 21:20
поделиться

Другой взгляд на ответ Даррона , вот как я это делаю:

IN="bla@some.com;john@home.com"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)
30
ответ дан Community 22 October 2018 в 21:20
поделиться

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

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

Посмотрите:

$ in= 

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

$ in='one;two;three;'    # there's 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 (он же readarray) поддерживает опцию -d для указания разделителя. Отсюда другой канонический путь:

mapfile -d ';' -t array < <(printf '%s;' "$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's 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 (он же readarray) поддерживает опцию -d для указания разделителя. Отсюда другой канонический путь:

mapfile -d ';' -t array < <(printf '%s;' "$in")
26
ответ дан gniourf_gniourf 22 October 2018 в 21:20
поделиться

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

echo "bla@some.com;john@home.com" | awk -F';' '{print $1,$2}'

даст

bla@some.com john@home.com

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

42
ответ дан noamtm 22 October 2018 в 21:20
поделиться

Взято из Разделенный массив сценариев оболочки 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;
869
ответ дан Community 22 October 2018 в 21:20
поделиться
echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com
64
ответ дан 22 November 2019 в 20:06
поделиться

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

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

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

230
ответ дан 22 November 2019 в 20:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: