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


109

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

variable=abc,def,ghij

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

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


Что именно вы хотите сделать повторением?
SMA

1
Я хочу передать каждое поле (например, abc) в качестве аргумента процедуры.
Раманатан К.

Ответы:


128

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

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"выше вызова между циклом for doи doneвнутри цикла вы можете вызвать свою процедуру proc "$i".


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


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


8
Это не работает, если $variableсодержит пробелы, например variable=a b,c,d=> печатает 4 строки (a | b | c | d), а не 3 (ab | c | d)
Дэн

А также добавляются новые кавычки, такие как ** "value **. не могли бы вы обновить, если есть какое-нибудь регулярное выражение для удаления этого ..
gks

139

Не возиться с 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


4
Самое приятное в этом то, что вы можете вкладывать несколько циклов, используя разные разделители. Хорошее предложение!
Брэд Паркс,

6
Хороший совет, как избежать путаницы IFS!
Laimoncijus

14
Небольшое предупреждение - если какое-либо из значений в $iсодержит пробелы, они будут разделены (и это может быть причиной того, что оно было передано как разделенное запятыми, а не разделенное пробелом)
Тоби Спейт

4
Если предыдущая функция изменила настройку IFS, то это не сработает ... Измените ее на это:for i in ${variable//,/$IFS} do; echo "$i"; done
Joep

2
Когда я пробую этот подход, я получаю сообщение об ошибке «Плохая замена».
Lost Crotchet

56

Если вы установите другой разделитель полей, вы можете напрямую использовать 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 --

Также см. Stackoverflow.com/questions/918886/… :-) Удачи всем.
Shellter

Ага! Я тоже думал об этом, только тогда потребовалось выполнить шаги: a whileдля чтения в массив и еще один для просмотра результатов.
fedorqui 'SO stop harming'

3
Меня радует, когда кто-то действительно знает, как использовать оболочку, а не собирать вместе кучу binutils.
ПетерТ

ты должен IFSвернуться к тому, что было раньше?
пстантон

1
@fedorqui Так и было! Это единственная переменная, которую я хотел перебрать, и это упростило чтение моего yaml-файла (вместо одной LONG переменной среды у него есть группа строк)
GammaGames

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

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

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

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


1

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

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

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


0

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

#/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

а что, если я не уверен в количестве полей в переменной testpid?
Ramanathan K

Кроме того, мне нужно передать каждое поле указанной выше переменной в качестве аргумента процедуры. И я хочу делать это, пока длина переменной не станет равной нулю. (т.е. все поля должны быть переданы в процедуру обработки один раз)
Раманатан К.

Я не знаю о процедуре. По этой ссылке Вы можете узнать количество полей. http://stackoverflow.com/questions/8629330/unix-count-of-columns-in-file. После этого превратите цикл for в цикл while. Вы можете получить это.
Karthikeyan.RS

Я думаю, это для подсчета полей из файла. Что, если мне нужно подсчитать поля в переменной (предположим, переменная testpid = abc, def, ghij)
Раманатан К.

повторить переменную и направить вывод в awk. Или посмотрите мой обновленный ответ. Может быть полезно.
Karthikeyan.RS

0

Другое решение, не использующее 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

Это работает правильно в bash, однако zsh проглатывает пробелы.
lleaff

0

Вот мое чистое решение 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
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.