Проведіть цикл через змінну оболонку, розділену комою


109

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

variable=abc,def,ghij

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

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


Що саме ви хочете зробити, повторивши?
SMA

1
Я хочу передати кожне поле (скажімо, abc) як аргумент до процедури.
Ramanathan K

Відповіді:


125

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

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а потім проаналізуйте вашу змінну.


Сподіваюся, це допомагає.


8
Це не працює, якщо $variableмістить пробіл, наприклад variable=a b,c,d=> друкує 4 рядки (a | b | c | d), а не 3 (ab | c | d)
Dan

А також до нього додаються нові котирування, як ** "value **. Ви можете будь-ласка оновити, якщо є який-небудь регулярний вираз, щоб видалити це ..
gks

136

Не возитися з IFS
Не викликає зовнішню команду

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

Використання bash string manipulation http://www.tldp.org/LDP/abs/html/string-manipulation.html


3
Приємно в тому, що ви можете вкласти декілька петель, використовуючи різні роздільники. Гарна пропозиція!
Бред Паркс

5
Приємна порада для уникнення безладу IFS!
Laimoncijus

13
Невелике попередження - якщо будь-яке значення $iмістить пробіли, вони будуть розділені (і це може бути причиною того, що воно передається як розділений комами, а не розділений пробілом)
Toby Speight

4
Якщо попередня функція змінила налаштування IFS, то це не працює ... Змініть це на це:for i in ${variable//,/$IFS} do; echo "$i"; done
Joep

2
Я отримую повідомлення про помилку "Погана заміна", коли я спробую такий підхід.
Загублений Crotchet

55

Якщо встановити інший роздільник поля, ви можете безпосередньо використовувати 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/… :-) Успіхів усім.
обстріл

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

3
Робить мене щасливим, коли хтось насправді знає, як використовувати оболонку, а не з’єднувати купу бінутів.
PeterT

чи потрібно IFSповертатися до того, що було раніше?
pstanton

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

Також мені потрібно передавати кожне подане вищевказаної змінної як аргумент до процедури. І я хочу це робити, поки довжина змінної не стане нульовою. (тобто всі поля повинні бути передані один раз до процедури обробки)
Ramanathan K

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

Це для підрахунку полів із файлу, який я здогадуюсь. Що робити, якщо мені потрібно порахувати поля в змінній (припустимо, змінна є testpid = abc, def, ghij)
Ramanathan K

відлунювати змінну та передавати вихід на 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 проковтує пробіли.
лелеф

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.