我编写了很多临时脚本来向 sudoers、sysctl.conf 等添加行。通常我使用 lineinfile ansible 模块。然而,有时我无法这样做,所以我使用:
WHAT_I_WANT='my line of text = something'
WHAT_TO_REPLACE='my line of text = .*'
FILE_TO_EDIT=conf_file.conf
if ( ! grep -q "^$WHAT_I_WANT\$" FILE_TO_EDIT ); then
echo "$WHAT_I_WANT" >> FILE_TO_EDIT
else
sed -i "s/$WHAT_TO_REPLACE/$WHAT_I_WANT/g' FILE_TO_EDIT
fi
我的猜测是有一种更有效的方法来做到这一点。理想情况下使用一行和一种语言(并且还以允许正则表达式匹配、删除注释掉的行和备份文件的方式)但我不知道那是什么。
答案1
- 在我看来,使用超长变量名来替换长字符串会适得其反。
- 要在 grep/sed 命令中扩展变量,请使用双引号。
.*
在变量赋值中使用通配符将进行文件名扩展(通配符)- 以下脚本在成功
&&
或失败时使用条件执行||
,而不是 if。
REPL='something'
PATT='my line of text = '
FILE=conf_file.conf
cat $FILE
echo '========='
grep -q "^$PATT" $FILE && (sed -i "s/^$PATT.*$/$PATT$REPL/" $FILE) || (echo "$PATT$REPL" >> $FILE)
cat $FILE
答案2
实际上不是一句简单的话,但您可以通过一次调用来完成此操作sed
:
sed -e '
/pat/{s//repl/g;h;}
$!b
G
s/\n..*//;t
s/$/repl/
' yourfile
注意:我特意分别使用pat
和repl
来表示模式和替换,而不是像您那样使用 shell 变量。基本上有两个主要原因,它们会妨碍 sed 代码流+为了正确性,我们需要引用两者,并且不同的是,才能使它们工作。那项工作我就交给你了。
流程:比如说,文件从来没有 /pat/ 然后所有行都被 $!b 命令带到 stdout,并且当用 G 附加时最后一行只是看到一个空保留,因此t
不被获取,并且我们有一个附加操作。
当我们看到 /pat/ 线时,则它是 s///-ed 并标记了保留区域。如果它不是最后一行,我们只需突破到标准输出即可。对于 eof,我们对保持进行检查,并且由于它非空(假设repl
是 )NONEMPTY
,因此在从模式空间中删除保持后将采用测试路径。
Perl
提供了与代码中逐字意图匹配的清晰度:
perl -lne '$a += s/pat/repl/g,print}{print q[repl] unless $a'
解读为:该变量$a
用作替换次数的计数器。最后,当之前没有进行任何替换时,我们将追加到文件中。
至于通过变量提供模式/替换信息,我们可以这样做:
WHAT_I_WANT='my line of text = something'
WHAT_TO_REPLACE='my line of text = .*'
FILE_TO_EDIT=conf_file.conf
perl -li -sn -e '
$a += s/$pat/$repl/g,print}{print $repl unless $a
' -- -pat="$WHAT_I_WANT" -repl="$WHAT_TO_REPLACE" -- "$FILE_TO_EDIT"