为什么在用换行符替换空格后必须再次调用 sed ?

为什么在用换行符替换空格后必须再次调用 sed ?

给定一个文件,我想以八进制格式列出它包含的所有字节,每个字节都在自己的行上。

为什么这有效:

#!/bin/sh --

newline='
'

od -v -A n -t o1 -- /tmp/file | \
sed -e "s/ /\\${newline}/g" | \
sed -e '/^$/d'

而不是这个:

#!/bin/sh --

newline='
'

od -v -A n -t o1 -- /tmp/file | \
sed -e "s/ /\\${newline}/g" -e '/^$/d'

为什么我必须分别调用 sed 两次才能完成工作?

作为参考,未经进一步处理的 的输出od可能如下所示:

         047 124 167 141 163 040 142 162 151 154 154 151 147 054 040 141
         156 144 040 164 150 145 040 163 154 151 164 150 171 040 164 157
         166 145 163 012 011 104 151 144 040 147 171 162 145 040 141 156
         144 040 147 151 155 142 154 145 040 151 156 040 164 150 145 040
         167 141 142 145 072 012 101 154 154 040 155 151 155 163 171 040
         167 145 162 145 040 164 150 145 040 142 157 162 157 147 157 166
         145 163 054 012 011 101 156 144 040 164 150 145 040 155 157 155
         145 040 162 141 164 150 163 040 157 165 164 147 162 141 142 145
         056 012

答案1

每个sed表达式作用于整个模式空间。在循环开始时,将一行读入模式空间,然后sed将每个给定表达式应用于该数据。

在代码的第二个变体中,您通过替换将换行符插入到模式空间中。第二个表达式/^$/d仍将作用于整个模式空间,而不是作用于模式空间中的每一行,这意味着模式将不匹配(因为缓冲区非空),并且模式空间将不匹配。被删除。更改/^$/ds/\n\{2,\}//g将删除任何两个或多个连续换行符(这些将在输出中创建空行),这已经工作了。

这与代码的第一个变体形成对比,其中第二个变体sed读取第一个的输出sed。在这种情况下,第二个sed将读取每个生成的行单独地,并删除空的。

简而言之:将换行符添加到模式空间中不会使sed每个生成的行重新考虑为其余表达式的单独输入。


替代解决方案:

od -v -A n -t o1 -- /tmp/file |
sed -e 's/ \{2,\}//g' -e 'y/ /\n/'

第一个sed表达式s/ \{2,\}//g删除任何两个或多个连续空格,然后第二个表达式将剩余空格转换为换行符(\n该命令允许y,即使在 POSIX 中也是如此sed)。

或者,您可以在将所有剩余空格转换为换行符之前删除所有侧翼空格:

od -v -A n -t o1 -- /tmp/file |
sed -e 's/^ *//' -e '$s/ *$//' -e 'y/ /\n/'

(请注意,只有最后一行输出od可能有尾随空格,这就是我用作$第二个表达式的地址的原因。)

或者,使用以下命令可能会更直接awk

od -v -A n -t o1 -- /tmp/file |
awk '{ for (i = 1; i <= NF; ++i) print $i }'

答案2

这是因为在进行替换后,空行仅存在于输出文件中。为什么不

od -v -An -to1 -w1 -- file 

相关内容