循环使用逗号分隔的shell变量


109

假设我有一个Unix shell变量,如下所示

variable=abc,def,ghij

我想提取的所有值(abcdefghij使用for循环),并且每个值传递到一个过程。

该脚本应允许从中提取任意数量的逗号分隔值$variable


您到底想通过迭代做什么?
SMA 2014年

1
我想将每个字段(例如abc)作为参数传递给过程。
Ramanathan K

Answers:


125

您可以使用以下脚本来动态遍历变量,无论变量有多少个字段(只要以逗号分隔即可)。

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

在for循环和for循环之间,可以代替您echo "$i"上面的调用,而是调用procedure 。dodoneproc "$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字符串操作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/… :-)祝大家好运。
剥壳机2014年

是的 我还考虑了这一点,仅此而已,它需要执行以下步骤:将a while读入数组,然后再遍历结果。
fedorqui'SO停止伤害

3
当有人真正知道如何使用外壳程序,而不是通过管道将一堆binutils组合在一起时,让我感到高兴。
PeterT

您是否必须IFS回到以前的状态?
pstanton '18

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 2014年

另外,我需要将上述变量的每个字段作为过程的参数传递。我想这样做直到变量的长度变为零。(即,所有字段都应传递给处理程序一次)
Ramanathan K

我不知道该程序。通过此链接,您可以获取字段数。http://stackoverflow.com/questions/8629330/unix-count-of-columns-in-file。之后,将for循环到while循环中。你可以得到那个。
Karthikeyan.RS 2014年

这是从我猜想文件中的字段。如果我需要计算变量中的字段(假设变量为testpid = abc,def,ghij),该怎么办
Ramanathan K

回显变量并将输出通过管道传递到awk。否则,请参阅我的最新答案。可能有用。
Karthikeyan.RS 2014年

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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.