我需要在配置文件的末尾添加以下一行:
include "/configs/projectname.conf"
到一个名为lighttpd.conf的文件
我正在研究使用sed来做到这一点,但我不知道如何。
我怎么能只插入它,如果行已经不存在?
我需要在配置文件的末尾添加以下一行:
include "/configs/projectname.conf"
到一个名为lighttpd.conf的文件
我正在研究使用sed来做到这一点,但我不知道如何。
我怎么能只插入它,如果行已经不存在?
当前回答
只使用sed,我建议使用以下解决方案:
sed -i \
-e 's#^include "/configs/projectname.conf"#include "/configs/projectname.conf"#' \
-e t \
-e '$ainclude "/configs/projectname.conf"' lighttpd.conf
将include“/configs/projectname.conf”行替换为自身(此处使用#作为分隔符)
T如果替换成功,跳过其余命令
$a否则跳转到最后一行,并在其后添加include "/configs/projectname.conf "
其他回答
另一个sed解决方案是始终将它附加到最后一行,并删除一个预先存在的。
sed -e '$a\' -e '<your-entry>' -e "/<your-entry-properly-escaped>/d"
“适当转义”意味着放入一个匹配你的条目的正则表达式,即从你的实际条目中转义所有的正则表达式控件,即在^$/*?+()前面放一个反斜杠。
这可能会在文件的最后一行失败,或者如果没有悬空换行符,我不确定,但这可以通过一些漂亮的分支来处理…
使用sed,最简单的语法:
sed \
-e '/^\(option=\).*/{s//\1value/;:a;n;ba;q}' \
-e '$aoption=value' filename
如果参数存在,这将替换它,否则将把它添加到文件的底部。
如果您想就地编辑文件,请使用-i选项。
如果你想接受并保留空白,并且除了删除注释之外,如果行已经存在,但被注释掉了,那么写:
sed -i \
-e '/^#\?\(\s*option\s*=\s*\).*/{s//\1value/;:a;n;ba;q}' \
-e '$aoption=value' filename
请注意,选项和值都不能包含斜杠/,否则必须将其转义为\/。
要使用bash变量$option和$value,你可以这样写:
sed -i \
-e '/^#\?\(\s*'${option//\//\\/}'\s*=\s*\).*/{s//\1'${value//\//\\/}'/;:a;n;ba;q}' \
-e '$a'${option//\//\\/}'='${value//\//\\/} filename
bash表达式${option//\//\\/}引用斜杠,它将所有/替换为\/。
注意:刚刚陷入了一个问题。在bash中,你可以引用"${option//\//\\/}",但在busybox的sh中,这行不通,所以你应该避免使用引号,至少在非boure -shell中是这样。
所有这些组合在一个bash函数中:
# call option with parameters: $1=name $2=value $3=file
function option() {
name=${1//\//\\/}
value=${2//\//\\/}
sed -i \
-e '/^#\?\(\s*'"${name}"'\s*=\s*\).*/{s//\1'"${value}"'/;:a;n;ba;q}' \
-e '$a'"${name}"'='"${value}" $3
}
解释:
/^\(option=\).*/: Match lines that start with option= and (.*) ignore everything after the =. The \(…\) encloses the part we will reuse as \1later. /^#?(\s*'"${option//////}"'\s*=\s*).*/: Ignore commented out code with # at the begin of line. \? means «optional». The comment will be removed, because it is outside of the copied part in \(…\). \s* means «any number of white spaces» (space, tabulator). White spaces are copied, since they are within \(…\), so you do not lose formatting. /^\(option=\).*/{…}: If matches a line /…/, then execute the next command. Command to execute is not a single command, but a block {…}. s//…/: Search and replace. Since the search term is empty //, it applies to the last match, which was /^\(option=\).*/. s//\1value/: Replace the last match with everything in (…), referenced by \1and the textvalue` :a;n;ba;q: Set label a, then read next line n, then branch b (or goto) back to label a, that means: read all lines up to the end of file, so after the first match, just fetch all following lines without further processing. Then q quit and therefore ignore everything else. $aoption=value: At the end of file $, append a the text option=value
关于sed和命令概述的更多信息请参阅我的博客:
https://marc.wackerlin.ch/computer/stream-editor-sed-overview-and-reference
sed -i 's/^option.*/option=value/g' /etc/fdm_monitor.conf
grep -q "option=value" /etc/fdm_monitor.conf || echo "option=value" >> /etc/fdm_monitor.conf
这将是一个干净、易读和可重用的解决方案,使用grep和echo仅在文件不存在时才向文件添加一行:
LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"
如果需要匹配整行,请使用grep -xqF
添加-s来忽略文件不存在时的错误,用这一行创建一个新文件。
我需要编辑一个文件限制写权限,所以需要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