如何使用 sed 准备值对来循环?

问题描述 投票:0回答:4

我写了

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
...

如何正确准备值对?

bash sed foreach
4个回答
2
投票

您正在使用命令替换并且未设置 IFS,请尝试:

IFS='\n'
for pair in $(my command | sed -En "s/my expression/\1 \2/p"); do
    echo $pair
done

默认是按空格、制表符和换行符分割。在这里,您只想在换行符上拆分,因此您需要将 IFS 设置为该值,否则这意味着如果对包含空格或制表符,则无法正常工作


1
投票

原因很简单,

for pair in $(…)

迭代空白(在您的情况下包括单个空格)分隔符。由于

$()
中的命令一次性求值,为
for
循环提供结果,因此它只会看到您提供的参数列表。因此,在开始
for
循环之前,请设置
IFS=$'\n'

IFS=$'\n'
for pair in $(…)

0
投票

如果不是一个非常简单的替代品,我会尽量避免

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)

0
投票

简单的解决方案是在

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
不是标准选项)。

© www.soinside.com 2019 - 2024. All rights reserved.