我需要在配置文件的末尾添加以下一行:
include "/configs/projectname.conf"
到一个名为lighttpd.conf的文件
我正在研究使用sed来做到这一点,但我不知道如何。
我怎么能只插入它,如果行已经不存在?
我需要在配置文件的末尾添加以下一行:
include "/configs/projectname.conf"
到一个名为lighttpd.conf的文件
我正在研究使用sed来做到这一点,但我不知道如何。
我怎么能只插入它,如果行已经不存在?
保持简单就好:)
Grep + echo就足够了:
grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar
-q安静点 -x匹配整行 -F pattern是一个普通字符串 https://linux.die.net/man/1/grep
编辑: 合并了@cerin和@thijs-wouters的建议。
使用awk
awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file
下面是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
下面是一个awk实现
/^option *=/ {
print "option=value"; # print this instead of the original line
done=1; # set a flag, that the line was found
next # all done for this line
}
{print} # all other lines -> print them
END { # end of file
if(done != 1) # haven't found /option=/ -> add it at the end of output
print "option=value"
}
使用
awk -f update.awk < /etc/fdm_monitor.conf > /etc/fdm_monitor.conf.tmp && \
mv /etc/fdm_monitor.conf.tmp /etc/fdm_monitor.conf
or
awk -f update.awk < /etc/fdm_monitor.conf | sponge /etc/fdm_monitor.conf
编辑: 一句话:
awk '/^option *=/ {print "option=value";d=1;next}{print}END{if(d!=1)print "option=value"}' /etc/fdm_monitor.conf | sponge /etc/fdm_monitor.conf
下面是awk的一行代码:
awk -v s="option=value" '/^option/{f=1;$0=s}7;END{if(!f)print s}' file
这不会对文件进行就地更改,但是您可以:
awk '...' file > tmpfile && mv tmpfile file
试试这个:
grep -q '^option' file && sed -i 's/^option.*/option=value/' file || echo 'option=value' >> file
使用sed,你可以这样说:
sed -e '/option=/{s/.*/option=value/;:a;n;:ba;q}' -e 'aoption=value' filename
如果参数存在,这将替换它,否则将把它添加到文件的底部。
如果你想就地编辑文件,使用-i选项:
sed -i -e '/option=/{s/.*/option=value/;:a;n;:ba;q}' -e 'aoption=value' filename
sed -i '1 h
1 !H
$ {
x
s/^option.*/option=value/g
t
s/$/\
option=value/
}' /etc/fdm_monitor.conf
加载缓冲区中的所有文件,在结束时,更改所有发生的情况,如果没有发生变化,则添加到结束
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
我需要编辑一个文件限制写权限,所以需要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
另一个sed解决方案是始终将它附加到最后一行,并删除一个预先存在的。
sed -e '$a\' -e '<your-entry>' -e "/<your-entry-properly-escaped>/d"
“适当转义”意味着放入一个匹配你的条目的正则表达式,即从你的实际条目中转义所有的正则表达式控件,即在^$/*?+()前面放一个反斜杠。
这可能会在文件的最后一行失败,或者如果没有悬空换行符,我不确定,但这可以通过一些漂亮的分支来处理…
这将是一个干净、易读和可重用的解决方案,使用grep和echo仅在文件不存在时才向文件添加一行:
LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"
如果需要匹配整行,请使用grep -xqF
添加-s来忽略文件不存在时的错误,用这一行创建一个新文件。
作为awk专用的一行代码:
awk -v s=option=value '/^option=/{$0=s;f=1} {a[++n]=$0} END{if(!f)a[++n]=s;for(i=1;i<=n;i++)print a[i]>ARGV[1]}' file
ARGV[1]是你的输入文件。它被打开并写入end块的for循环中。在END块中打开文件进行输出,取代了像海绵或写入临时文件,然后将临时文件移动到文件等实用程序的需要。
对数组a[]的两次赋值将所有输出行累加到a中。if(!f)a[++n]=s如果主awk循环在文件中找不到option,则追加新的option=value。
为了可读性,我添加了一些空格(不是很多),但在整个awk程序中只需要一个空格,即打印后的空格。 如果文件包含#注释,它们将被保留。
为了减少重复,我通过设置变量详细阐述了kev的grep/sed解决方案。
设置第一行中的变量(提示:$_option将匹配一行中的所有内容,直到值[包括任何分隔符,如=或:])。
_file="/etc/ssmtp/ssmtp.conf" _option="mailhub=" _value="my.domain.tld" \ sh -c '\ grep -q "^$_option" "$_file" \ && sed -i "s/^$_option.*/$_option$_value/" "$_file" \ || echo "$_option$_value" >> "$_file"\ '
注意那个sh -c '…’只是有扩大变量范围的效果,而不需要导出。(参见在bash中的命令对管道中的第二个命令无效之前设置环境变量)
如果写入一个受保护的文件,@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"
下面是一个单行sed,它内联完成这项工作。注意,当变量存在时,它会保留变量的位置及其在文件中的缩进。这对于上下文来说通常很重要,比如当周围有注释时,或者当变量位于缩进块中时。任何基于“先删除再追加”范式的解决方案在这方面都失败得很厉害。
sed -i '/^[ \t]*option=/{h;s/=.*/=value/};${x;/^$/{s//option=value/;H};x}' test.conf
对于一个通用的变量/值对,你可以这样写:
var=c
val='12 34' # it handles spaces nicely btw
sed -i '/^[ \t]*'"$var"'=/{h;s/=.*/='"$val"'/};${x;/^$/{s//c='"$val"'/;H};x}' test.conf
最后,如果还想保留内联注释,可以使用catch组。例如,如果test.conf包含以下内容:
a=123
# Here is "c":
c=999 # with its own comment and indent
b=234
d=567
然后运行这个
var='c'
val='"yay"'
sed -i '/^[ \t]*'"$var"'=/{h;s/=[^#]*\(.*\)/='"$val"'\1/;s/'"$val"'#/'"$val"' #/};${x;/^$/{s//'"$var"'='"$val"'/;H};x}' test.conf
生产:
a=123
# Here is "c":
c="yay" # with its own comment and indent
b=234
d=567
使用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
如果有一天,其他人不得不将此代码作为“遗留代码”来处理,那么如果您编写了不那么开放的代码,那么这个人将会感激您,例如
grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi
你可以使用这个函数来查找和搜索配置更改:
#!/bin/bash
#Find and Replace config values
find_and_replace_config () {
file=$1
var=$2
new_value=$3
awk -v var="$var" -v new_val="$new_value" 'BEGIN{FS=OFS="="}match($1, "^\\s*" var "\\s*") {$2=" " new_val}1' "$file" > output.tmp && sudo mv output.tmp $file
}
find_and_replace_config /etc/php5/apache2/php.ini max_execution_time 60
使用grep得到的答案是错误的。你需要添加一个-x选项来匹配整行,否则像#text to add这样的行在寻找要添加的文本时仍然会匹配。
所以正确的解决方案是这样的:
grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar
使用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
使用联机sed
grep -qxF "port=9033" $lightconf || sed -i "$ a port=9033" $lightconf
在root下使用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
如果您想在Linux终端中使用python脚本运行此命令…
import os,sys
LINE = 'include '+ <insert_line_STRING>
FILE = <insert_file_path_STRING>
os.system('grep -qxF $"'+LINE+'" '+FILE+' || echo $"'+LINE+'" >> '+FILE)
$和双重报价让我陷入了困境,但这奏效了。 谢谢大家
Try:
LINE='include "/configs/projectname.conf"'
sed -n "\|$LINE|q;\$a $LINE" lighttpd.conf >> lighttpd.conf
使用管道作为分隔符,如果找到$LINE则退出。否则,在结尾附加$LINE。
因为我们只在sed命令中读取文件,所以我认为我们一般不存在clobber问题(这取决于您的shell设置)。
只使用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 "
几乎所有的答案都有效,但根据我的经验,并不是在所有场景或操作系统中都有效。以下是唯一适用于旧系统和新的不同口味的操作系统的东西。 我需要附加KUBECONFIG路径到bashrc文件,如果它不存在。我所做的是
我假设它存在,然后删除它。 使用sed附加我想要的字符串。
sed -i '/KUBECONFIG=/d' ~/.bashrc
echo 'export KUBECONFIG=/etc/rancher/rke2/rke2.yaml' >> ~/.bashrc