仅当行不存在时才将其追加到文件中


157

我需要将以下行添加到配置文件的末尾:

include "/configs/projectname.conf"

到一个名为 lighttpd.conf

我正在寻找sed用于执行此操作的方法,但无法解决。

如果该行不存在,我将如何插入?


如果您要编辑ini文件,则该工具crudini可能是一个不错的选择(但还不适合lighthttpd)
rubo77 '19

Answers:


291

保持简单:)

grep + echo应该足够了:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

编辑:纳入@cerin和@ thijs-wouters建议


2
我将-q开关添加到grep以抑制输出: grep -vq ...
Dave Kirby

1
仅供参考,使用-v和&&看起来并不可行,而|| 没有-v的作品。
bPizzi

3
这仅适用于非常简单的行。否则,grep会将行中的大多数非字母字符解释为模式,从而使其无法检测到文件中的行。
塞林2014年

2
美丽的解决方案。当然,这也适用于触发更复杂的表达式。我的人使用echo触发cat多行Heredoc进入配置文件。
Eric L.

2
-s如果文件不存在,请添加以忽略错误,仅使用该行创建一个新文件。
弗兰克

78

这是一个干净的,可读的和可重用的解决方案grepecho仅在尚不存在的情况下使用和将行添加到文件中:

LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"

如果您需要匹配整行,请使用 grep -xqF

-s如果文件不存在,请添加以忽略错误,仅使用该行创建一个新文件。


5
如果到需要root用户权限的文件:sudo grep -qF "$LINE" "$FILE" || echo "$LINE" | sudo tee -a "$FILE"
nebffa

当行以开头时,这实际上不起作用-
hyperknot

@zsero:好点!我添加--了grep命令,因此它将不再将以破折号开头的$ LINE解释为选项。
rubo77 '18 -2-7

13

这是一个sed版本:

sed -e '\|include "/configs/projectname.conf"|h; ${x;s/incl//;{g;t};a\' -e 'include "/configs/projectname.conf"' -e '}' file

如果您的字符串在变量中:

string='include "/configs/projectname.conf"'
sed -e "\|$string|h; \${x;s|$string||;{g;t};a\\" -e "$string" -e "}" file

对于使用完整/更新的配置文件条目替换部分字符串或根据需要添加行的命令:sed -i -e '\|session.*pam_mkhomedir.so|h; ${x;s/mkhomedir//;{g;tF};a\' -e 'session\trequired\tpam_mkhomedir.so umask=0022' -e '};:F;s/.*mkhomedir.*/session\trequired\tpam_mkhomedir.so umask=0022/g;' /etc/pam.d/common-session
bgStack15 '16

不错,它对我帮助很大+1。你能解释一下你的sed表达吗?
Rahul

我希望看到此解决方案的注释说明。我已经使用sed了很多年,但大部分只是s命令。该holdpattern空间互换是超越我。
通常

11

如果写入受保护的文件,@drAlberT和@ rubo77的答案可能对您不起作用,因为一个人不能sudo >>。那么,一个类似的简单解决方案将是使用tee --append(或在MacOS上tee -a):

LINE='include "/configs/projectname.conf"'
FILE=lighttpd.conf
grep -qF "$LINE" "$FILE"  || echo "$LINE" | sudo tee --append "$FILE"

6

如果有一天,其他人必须将此代码作为“旧版代码”来处理,那么如果您编写的代码不太公开,那么该人将不胜感激,例如

grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
  echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi

1
抱歉,如果您不了解shell,请去管理Windows。使用&&和||的解决方案 是标准的Shell语法,任何主管管理员都应理解。那里没有深奥的东西。
Graham Nicholls

3
感谢您的评论格雷厄姆!是的,这是正常的shell语法。是的,任何称职的管理员都应该理解它。但是,它基于外壳内表达式评估算法的副作用而工作。因此,您可以随心所欲地调用它,除了简单明了,这就是我的初衷。
Marcelo Ventura '18


6

另一个sed解决方案是始终将其附加在最后一行,并删除现有的。

sed -e '$a\' -e '<your-entry>' -e "/<your-entry-properly-escaped>/d"

“正确转义”表示放置与您的条目匹配的正则表达式,即从实际条目中转义所有正则表达式控件,即在^ $ / *?+()前面加反斜杠。

这可能在文件的最后一行失败,或者如果没有悬空的换行符,我不确定,但是可以通过一些漂亮的分支来解决...


1
精美的单线纸,简短易读。非常适合更新etc / hosts:sed -i.bak -e '$a\' -e "$NEW_IP\t\t$HOST.domain\t$HOST" -e "/.*$HOST/d" /etc/hosts
Noam Manos '18

一个较小的版本:sed -i -e '/<entry>/d; $a <entry>'
杰罗姆Pouiller

@Jezz我认为,如果该条目已经在文件末尾,则此操作将失败,因为如果删除恰好在最后一行,该d命令将重新启动sed程序,从而跳过append。
Robin479

@ Robin479天哪,删除a前必须执行ppend 。它与您的原始答案几乎相同。dsed -i -e '$a<entry>' -e '/<entry>/d'
杰罗姆Pouiller

3

使用awk

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file

是“文件文件”还是“文件”?
webdevguy 2014年

这是file file因为它会使同一文件两次通过。NR==FNR第一遍是正确的,但第二遍不是。这是常见的Awk习语。
三胞胎

1

使用grep的答案是错误的。您需要添加-x选项以匹配整行,否则#text to add在希望精确添加时仍会匹配text to add

所以正确的解决方案是这样的:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

1

使用sed:它将插入到行尾。当然,您也可以照常传递变量。

grep -qxF "port=9033" $light.conf
if [ $? -ne 0 ]; then
  sed -i "$ a port=9033" $light.conf
else
    echo "port=9033 already added"
fi

使用oneliner sed

grep -qxF "port=9033" $lightconf || sed -i "$ a port=9033" $lightconf

在根目录下使用echo可能无法工作,但可以这样工作。但是,如果您要执行此操作,则它不会使您自动化,因为它可能会要求输入密码。

当我尝试从根目录为特定用户进行编辑时遇到问题。仅添加$username之前对我来说是一个解决方法。

grep -qxF "port=9033" light.conf
if [ $? -ne 0 ]; then
  sudo -u $user_name echo "port=9033" >> light.conf
else
    echo "already there"    
fi

欢迎来到stackoverflow!在发布答案之前,请仔细阅读问题
-OP

-1

我需要编辑具有受限写权限的文件,因此需要这样做sudo。从ghostdog74的答案并使用临时文件进行工作:

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file > /tmp/file
sudo mv /tmp/file file

看起来不正确,当缺少配置行时,它将丢失文件中的其他行。
三胞胎
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.