我写了
for pair in $(my command | sed -En "s/my expression/\1 \2/p"); do
echo $pair
done
但是每行都有每个条目:
host
port
host
port
...
如果我打印
my command | sed -En "s/my expression/\1 \2/p"
我明白了
host port
host port
host port
...
如何正确准备值对?
您正在使用命令替换并且未设置 IFS,请尝试:
IFS='\n'
for pair in $(my command | sed -En "s/my expression/\1 \2/p"); do
echo $pair
done
默认是按空格、制表符和换行符分割。在这里,您只想在换行符上拆分,因此您需要将 IFS 设置为该值,否则这意味着如果对包含空格或制表符,则无法正常工作
原因很简单,
for pair in $(…)
迭代空白(在您的情况下包括单个空格)分隔符。由于
$()
中的命令一次性求值,为 for
循环提供结果,因此它只会看到您提供的参数列表。因此,在开始 for
循环之前,请设置 IFS=$'\n'
。
IFS=$'\n'
for pair in $(…)
如果不是一个非常简单的替代品,我会尽量避免
sed
。 awk
功能更强大,同时也更清晰。这是 POSIX awk
答案。
my command | awk '{ d="\n"; if (NR%2) d=" "; printf $0 d}'
但是,如果将其放入命令替换(
$(…)
格式)中,您将无法保留间距。要解决这个问题,请考虑使用不同的成对分隔符,例如将 d=" "
更改为 d=":"
for pair in "$(my command | awk '{ d="\n"; if (NR%2) d=":"; printf $0 d}')"; do
echo $pair
done
如果您确实想对这对的不同部分执行某些操作,请在 bash 中执行整个操作:
line_num=0
while IFS= read line; do
line_num=$((line_num+1))
if [ $((line_num%2)) = 1 ]; then
host="$line"
else
port="$line"
# do things with $host and $port here
echo "$host $port"
fi
done < <(my command)
简单的解决方案是在
sed
循环之前运行 while read
before。
my command |
sed -n 's/my expression/\1 \2/p' |
while read -r host port; do
: something with "$host" and "$port"
done
这可以一直移植到原来的 Bourne
sh
。
(Bash 提供
while ... done <(process)
,它有一些额外的好处,但它不能与 sh
一起移植。)
就像另一个答案已经指出的那样,
for value in $(generate expressions); do
会将
generate expressions
的输出解析为 标记, 并一次循环它们一个。这是 不使用 for
读取行的原因之一,但当然,在这种情况下,我们甚至不处理行。
也许还请注意
read -r
的使用(你基本上总是想要这个,除非你特别在遗留read
的输入中使用反斜杠的明显奇怪的行为)和sed
脚本周围的单引号。
我还从您的
-E
选项中删除了 sed
,因为在本例中您没有使用任何正则表达式语法,这会产生任何差异(-E
选择的 ERE 正则表达式方言比默认的 BRE 更熟悉和方便) ,但 -E
不是标准选项)。