sed输出如何像printf的格式化打印一样格式化?

sed输出如何像printf的格式化打印一样格式化?

sed 可以用类似于 printf 格式化打印的字符串替换文本吗?

以下 sed 命令将以变量中指定的多个值替换以“$domain”的当前值开头的行。

/bin/sed  "s/\(^${domain} *${limittype} * ${limititem}.*\)/$EXPL#\1\n${domain} ${limittype} ${limititem} ${value}/" /etc/security/limits.conf

但是,由于域等值的长度不同,输出将无法正确对齐。

所以输出会是这样的

#oracle   hard   nproc    131072
oracle hard nproc 666

虽然有效,但很难阅读。我更喜欢得到类似的东西

#oracle   hard   nproc    131072
oracle   hard   nproc    666

我能得到的最好的输出是:

/bin/sed  "s/\(^${domain}\)\( *\)\(${limittype}\)\( *\)\(${limititem}\)\( *\)\(.*\)/$EXPL#\1\2\3\4\5\6\7\n${domain}\2${limittype}\4${limititem}\6${value}/" /etc/security/limits.conf

但我相信必须有一种更优雅的方式来做到这一点。

sed oneliners 文档包含一些使用指定字符数的示例,例如

sed -e :a -e 's/^.\{1,78\}$/ &/;ta'  # set at 78 plus 1 space

但这是在regexp章节中而不是在replacement章节中。

答案1

虽然理论上您可以完全在 sed 中完成此操作(因为它是图灵完备的),但这并不是完成这项工作的正确工具。

一个简单的方法是在 sed 中插入制表符,然后将它们后处理为空格。如果您可以确定所有列的位置,请将 sed 输出通过管道传输expand

</etc/security/limits.conf \
sed  "s/\(^${domain} *${limittype} * ${limititem}.*\)/$EXPL#\1\n${domain}\t${limittype}\t${limititem}\t${value}/" |
expand -t 10,17,26

\t(如果您的 sed 不支持,请使用文字制表符\t。)

如果您事先不知道列宽,请尝试 BSDcolumn公用事业。它查看整个输入文件以确定容纳所有行长度的列宽。

</etc/security/limits.conf \
sed  "s/\(^${domain} *${limittype} * ${limititem}.*\)/$EXPL#\1 ${domain} ${limittype} ${limititem} ${value}/" |
column -t

如果您的 sed 脚本重写了注释掉的行和非注释掉的行,或者如果您使用column,则需要进行一些后处理以使注释掉的行倾斜注释标记的宽度。

… | sed '/^#/ s/ //'

您可以使用 awk 代替。它有一个printf功能。作为额外的好处,有一种简单的方法可以保护搜索列内容中的特殊字符,例如.或。*

</etc/security/limits.conf awk -v domain="$domain" -v limittype="$limittype" -v limititem="$limititem" -v value="$value" '
$1 == domain && $2 == limittype && $3 == limititem  {
    printf "#%-9s %-8s %-9s %s\n%-9s %-8s %-9s %s\n", $1, $2, $3, $4, $1, $2, $3, value; next
}
1 {print}
'

答案2

这使用了扩展的正则表达式语法-r,它消除了很多混乱。另外,因为您已经知道一些字段值,所以实际上不需要反向引用它们,这再次减少了混乱(和开销)。

&是一个特殊的替换值:它保存整个匹配的模式。使用&, 再次减少混乱。由于它不是反向引用,因此开销显着减少。

我用过( +)vs ( *)..假设+输入字段之间至少有一个空格。只要将其更改为事实*并非如此。

EXPL=
dom=oracle
typ=hard
itm=nproc
val=666

echo "oracle   hard   nproc    131072" |
  sed -r "s/^$dom( +)$typ( +)$itm( +).*/$EXPL#&\n$dom\1$typ\2$itm\3$val/" 

输出

#oracle   hard   nproc    131072
oracle   hard   nproc    666

答案3

只需使用 printf 格式化 sed 输出:

printf "%5s %12s %4s\n" $(sed 's/.../.../')

相关内容