通过逗号分隔的 shell 变量进行循环

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

variable=abc,def,ghij

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

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

215428 次浏览

试试这个。

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

可以使用下面的脚本动态遍历变量,无论变量有多少个字段,只要变量之间只用逗号分隔即可。

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

您可以在 for 循环中的 dodone之间调用过程 proc "$i",而不是上面的 echo "$i"调用。


更新 : 如果变量的值不包含空格,则上面的代码片段可以工作。如果您有这样的要求,请使用其中一个解决方案,可以改变 IFS,然后解析您的变量。


希望这个能帮上忙。

如果设置了不同的字段分隔符,可以直接使用 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 --

不是干扰 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

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


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

我更喜欢使用 tr 而不是 sed,因为 sed 在某些情况下会遇到特殊字符(如 r n)的问题。

另一种解决方案是将 IFS 设置为特定的分离器

另一种不使用 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

下面是一个不使用 echo 的基于 tr 的替代解决方案,表示为一行程序。

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

没有回音感觉更整洁,但这只是我个人的喜好。

下面是我的纯 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
}

解决办法如下:

  • 不需要干扰 IFS
  • 不需要辅助变量(如 for循环中的 i)
  • 应该很容易扩展以适用于多个分隔符(在模式中使用类似于 [:,]的括号表达式)
  • 实际上只在指定的分隔符上分裂,而不像在例如空格上提出的其他解决方案那样。
  • 是 POSIX 兼容的
  • 当 bash 的 nocasematch处于打开状态并且在与 ${parameter/pattern/string}case之类的匹配中使用具有小写/大写版本的分隔符时,不会遇到任何可能出现的微妙问题

当心:

  • 但是它可以处理变量本身,并从中弹出每个元素——如果不需要,则需要一个 helper 变量
  • 它假设设置了 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

注意,在模式中作为特殊字符的分隔符(例如文字 *)需要相应地引用。

我认为从语法上来说,这是更干净的,并且也通过了 shell 检查衬里

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