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/.../.../')