Цикл через переменную оболочки, разделенную запятыми

Предположим, у меня есть переменная оболочки Unix, как показано ниже

variable=abc,def,ghij

Я хочу извлечь все значения (abc, def и ghij) с помощью цикла for и передать каждое значение в процедуру.

Скрипт должен позволять извлекать произвольное количество значений, разделенных запятыми, из $variable.


person Ramanathan K    schedule 30.12.2014    source источник
comment
Что именно вы хотите сделать повторением?   -  person SMA    schedule 30.12.2014
comment
Я хочу передать каждое поле (скажем, abc) в качестве аргумента процедуры.   -  person Ramanathan K    schedule 30.12.2014


Ответы (9)


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

variable=abc,def,ghij
for i in $(echo $variable | sed "s/,/ /g")
do
    # call your procedure/other scripts here below
    echo "$i"
done

Вместо вызова echo "$i", приведенного выше, между do и done внутри цикла for вы можете вызвать свою процедуру proc "$i".


Обновление: приведенный выше фрагмент работает, если значение переменной не содержит пробелов. Если у вас есть такое требование, воспользуйтесь одним из решений, которые могут изменить IFS, а затем проанализируйте вашу переменную.


Надеюсь это поможет.

person anacron    schedule 30.12.2014
comment
Это не работает, если $variable содержит пробелы, например. variable=a b,c,d = ›печатает 4 строки (a | b | c | d), а не 3 (a b | c | d) - person Dan; 11.06.2015
comment
А также добавляются новые кавычки, такие как ** _1 _ **. не могли бы вы обновить, если есть какое-либо регулярное выражение, чтобы удалить это .. - person gks; 20.03.2017

Не возиться с IFS
Не вызывать внешнюю команду

variable=abc,def,ghij
for i in ${variable//,/ }
do
    # call your procedure/other scripts here below
    echo "$i"
done

Использование манипуляции со строкой в ​​bash http://www.tldp.org/LDP/abs/html/string-manipulation.html

person neric    schedule 09.03.2016
comment
Самое приятное в этом то, что вы можете вкладывать несколько циклов, используя разные разделители. Хорошее предложение! - person Brad Parks; 07.04.2016
comment
Хороший совет, как избежать путаницы с IFS! - person Laimoncijus; 11.08.2016
comment
Небольшое предупреждение - если какое-либо из значений в $i содержит пробелы, они будут разделены (и это может быть причиной того, что оно было передано через запятую, а не через пробел) - person Toby Speight; 23.08.2016
comment
Если предыдущая функция изменила настройку IFS, это не сработает ... Измените ее на это: for i in ${variable//,/$IFS} do; echo "$i"; done - person Joep; 13.09.2017
comment
Когда я пробую этот подход, я получаю сообщение об ошибке «Плохая замена». - person Lost Crotchet; 17.10.2018
comment
разве этот цикл не запускается только один раз? - person barlop; 11.05.2020
comment
@LostCrotchet здесь то же самое, потому что мы используем / bin / sh вместо / bin / bash - person Roel; 28.07.2020
comment
блин, каждый день узнаю что-то новое о bash! 10x человек! - person Xao; 27.04.2021

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

IFS=","
for v in $variable
do
   # things with "$v" ...
done

Вы также можете сохранить значения в массиве, а затем просмотреть его в цикле, как указано в Как разделить строку по разделителю в Bash? < / а>:

IFS=, read -ra values <<< "$variable"
for v in "${values[@]}"
do
   # things with "$v"
done

Тестовое задание

$ variable="abc,def,ghij"
$ IFS=","
$ for v in $variable
> do
> echo "var is $v"
> done
var is abc
var is def
var is ghij

В этом решении вы можете найти более широкий подход к Как перебирать список, разделенный запятыми, и выполнять команду для каждой записи .

Примеры второго подхода:

$ IFS=, read -ra vals <<< "abc,def,ghij"
$ printf "%s\n" "${vals[@]}"
abc
def
ghij
$ for v in "${vals[@]}"; do echo "$v --"; done
abc --
def --
ghij --
person fedorqui 'SO stop harming'    schedule 30.12.2014
comment
Также см. stackoverflow.com/questions/918886/ :-) Удачи всем. - person shellter; 30.12.2014
comment
Ага! Я тоже подумал об этом, только тогда потребовалось выполнить шаги: while для чтения в массив и еще один для просмотра результатов. - person fedorqui 'SO stop harming'; 30.12.2014
comment
Меня радует, когда кто-то действительно знает, как использовать оболочку, а не собирать вместе кучу binutils. - person PeterT; 22.02.2018
comment
вы должны вернуть IFS на то, что было раньше? - person pstanton; 06.09.2018
comment
@pstanton нет, потому что вы устанавливаете его только для области действия этой конкретной команды. Другое дело, если вы сделаете IFS=,, а затем read -ra vals <<< "a,b,c" в другой строке. См. Настройка IFS для одного оператора - person fedorqui 'SO stop harming'; 06.09.2018
comment
Я пытался использовать переменную среды из значения docker-compose, в котором использовался индикатор блокировки блока (>-), я использовал IFS=", " - person GammaGames; 12.07.2019
comment
@fedorqui Так и было! Это единственная переменная, которую я хотел перебрать, и это упростило чтение моего файла yaml (вместо одной переменной среды LONG у него есть группа строк) - person GammaGames; 15.07.2019

#/bin/bash   
TESTSTR="abc,def,ghij"

for i in $(echo $TESTSTR | tr ',' '\n')
do
echo $i
done

Я предпочитаю использовать tr вместо sed, потому что в некоторых случаях sed имеет проблемы со специальными символами, такими как \ r \ n.

другое решение - установить IFS на определенный разделитель

person Marek Lisiecki    schedule 09.04.2017

Другое решение, не использующее IFS и все еще сохраняющее пробелы:

$ var="a bc,def,ghij"
$ while read line; do echo line="$line"; done < <(echo "$var" | tr ',' '\n')
line=a bc
line=def
line=ghij
person user3071170    schedule 09.10.2018
comment
Это работает правильно в bash, однако zsh проглатывает пробелы. - person lleaff; 08.07.2020

Вот альтернативное решение на основе tr, которое не использует эхо, выраженное как однострочное.

for v in $(tr ',' '\n' <<< "$var") ; do something_with "$v" ; done

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

person 6EQUJ5    schedule 03.02.2019

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

loop_custom_delimited() {
    local list=$1
    local delimiter=$2
    local item
    if [[ $delimiter != ' ' ]]; then
        list=$(echo $list | sed 's/ /'`echo -e "\010"`'/g' | sed -E "s/$delimiter/ /g")
    fi
    for item in $list; do
        item=$(echo $item | sed 's/'`echo -e "\010"`'/ /g')
        echo "$item"
    done
}
person placidwolverine    schedule 11.12.2019

Следующее решение:

  • не нужно связываться с IFS
  • не нужны вспомогательные переменные (например, i в for-цикле)
  • должен быть легко расширяемым для работы с несколькими разделителями (с выражением в скобках, например [:,] в шаблонах)
  • действительно разделяется только на указанные разделители, а не - как некоторые другие решения, представленные здесь, например, пробелы тоже.
  • совместим с POSIX
  • не страдает от каких-либо тонких проблем, которые могут возникнуть, когда nocasematch в bash включен, а разделитель, имеющий версии в нижнем / верхнем регистре, используется в сопоставлении, например, с ${parameter/pattern/string} или case

остерегайтесь, что:

  • однако он работает с самой переменной и выталкивает из нее каждый элемент - если это нежелательно, необходима вспомогательная переменная
  • предполагается, что установлен var, и если это не так, а set -u действует, он завершится ошибкой
while true; do
        x="${var%%,*}"
        echo $x
        #x is not really needed here, one can of course directly use "${var%%:*}"

        if [ -z "${var##*,*}" ]  &&  [ -n "${var}" ]; then
                var="${var#*,}"
        else
                break
        fi
done

Помните, что разделители, которые будут специальными символами в шаблонах (например, литерал *), должны быть соответственно заключены в кавычки.

person calestyo    schedule 20.06.2021

Попробуй это.

#/bin/bash   
testpid="abc,def,ghij" 
count=`echo $testpid | grep -o ',' | wc -l` # this is not a good way
count=`expr $count + 1` 
while [ $count -gt 0 ]  ; do
     echo $testpid | cut -d ',' -f $i
     count=`expr $count - 1 `
done
person Karthikeyan.R.S    schedule 30.12.2014
comment
а что, если я не уверен в количестве полей в переменной testpid? - person Ramanathan K; 30.12.2014
comment
Кроме того, мне нужно передать каждое поле указанной выше переменной в качестве аргумента процедуры. И я хочу делать это до тех пор, пока длина переменной не станет равной нулю. (т.е. все поля должны быть переданы один раз в процедуру для обработки) - person Ramanathan K; 30.12.2014
comment
Я не знаю о процедуре. По этой ссылке Вы можете узнать количество полей. http://stackoverflow.com/questions/8629330/unix-count-of-columns-in-file. После этого превратите цикл for в цикл while. Вы можете получить это. - person Karthikeyan.R.S; 30.12.2014
comment
Я думаю, это для подсчета полей из файла. Что, если мне нужно подсчитать поля в переменной (предположим, что переменная testpid = abc, def, ghij) - person Ramanathan K; 30.12.2014
comment
повторить переменную и направить вывод в awk. Или посмотрите мой обновленный ответ. Может быть полезно. - person Karthikeyan.R.S; 30.12.2014
comment
grep: не распознанный флаг: o Использование: grep [-r] [-R] [-H] [-L] [-E | -F] [-c | -l | -q] [-insvxbhwyu] [- p [parasep]] -e список_образцов ... [-f файл_образца ...] [файл ...] Использование: grep [-r] [-R] [-H] [-L] [-E | -F ] [-c | -l | -q] [-insvxbhwyu] [-p [parasep]] [-e список_образцов ...] -f файл_образца ... [файл ...] Использование: grep [-r] [ -R] [-H] [-L] [-E | -F] [-c | -l | -q] [-insvxbhwyu] [-p [parasep]] список_образцов [файл ...] вырезать: флаг требуется параметр: f Использование: cut -b Список [-n] [Файл ...] или: cut -c Список [Файл ...] или: cut -f Список [-d Символ] [-s] [Файл ...] - person Ramanathan K; 30.12.2014
comment
Вы пробовали команду awk. ?? Это легко для тебя !. Потому что я использую Ubuntu. - person Karthikeyan.R.S; 30.12.2014
comment
Не могли бы вы помочь мне с командой, поскольку я новичок в сценариях оболочки - person Ramanathan K; 30.12.2014
comment
слишком много строк кода для простой задачи! - person Xao; 27.04.2021