如果不匹配,如何在sed中添加一行

时间:2013-11-28 13:51:41

标签: bash sed

我使用以下sed命令替换配置文件中的一些参数:

sed -i 's/^option.*/option=value/g' /etc/fdm_monitor.conf

现在我有一个问题。如果该行不存在,我想将其添加到文件的底部。

我用C程序中的popen来调用它。我尝试使用awk

11 个答案:

答案 0 :(得分:60)

试试这个:

grep -q '^option' file && sed -i 's/^option.*/option=value/' file || echo 'option=value' >> file

答案 1 :(得分:9)

使用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中,这不起作用,所以你应该避免引号,至少在非bourne-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=\).*/:以option=和(.*)开头的匹配行会忽略=之后的所有内容。 \( ... \)将我们稍后将重复使用的部分包含在\1中。
  • / ^#\?(\ s *'“$ {option //////}”'\ s * = \ s *)。* /:忽略注释掉#的代码行开始。 \?表示“可选”。评论将被删除,因为它位于\( ... \)中复制的部分之外。 \s*表示“任意数量的空格”(空格,制表符)。复制空格,因为它们位于\( ... \)内,因此您不会丢失格式。
  • /^\(option=\).*/{…}:如果匹配行/…/,则执行下一个命令。要执行的命令不是单个命令,而是块{…}
  • s//…/:搜索并替换。由于搜索字词为空//,因此它适用于最后一次匹配,即/^\(option=\).*/
  • s//\1value/: Replace the last match with everything in(...), referenced by \ 1 and the text value`
  • :a;n;ba;q:设置标签a,然后阅读下一行n,然后将b(或转到)分支回标签a,这意味着:读取文件的所有行,所以在第一次匹配后,只需获取所有后续行而无需进一步处理。然后q guit,因此忽略其他一切。
  • $aoption=value:在文件$的末尾,添加a文字option=value

有关sed和命令概述的更多信息,请访问我的博客:

答案 2 :(得分:4)

这是一个单行的sed,可以内联工作。请注意,会在文件存在时保留变量的位置及其缩进。这通常对于上下文很重要,例如当有变量或变量位于缩进块中时。任何基于“delete-then-append”范式的解决方案都会在此失败。

   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

答案 3 :(得分:3)

作为一个只有一个人的单行:

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]是您的输入file。它在for块的END循环中打开并写入。打开file以获取END块中的输出,取代了对sponge等实用程序的需求或写入临时文件,然后将mv临时文件file更改为a[]

数组a的两个赋值会将所有输出行累积到if(!f)a[++n]=s。如果主要awk循环在option=value中找不到optionfile会附加新的print

为了便于阅读,我添加了一些空格(不是很多),但是你需要在整个awk程序中只需要一个空格,即file之后的空格。 如果# comments包含JSONObject obj = new JSONObject(); ,则会保留这些内容。

答案 4 :(得分:2)

这是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

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

答案 5 :(得分:2)

这是一个awk单行:

 awk -v s="option=value" '/^option/{f=1;$0=s}7;END{if(!f)print s}' file

这不会对文件进行就地更改,但您可以:

awk '...' file > tmpfile && mv tmpfile file

答案 6 :(得分:2)

使用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

答案 7 :(得分:2)

 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

答案 8 :(得分:1)

sed -i '1 h
1 !H
$ {
   x
   s/^option.*/option=value/g
   t
   s/$/\
option=value/
   }' /etc/fdm_monitor.conf

将所有文件加载到缓冲区中,最后更改所有出现的内容,如果没有发生更改,请添加到结尾

答案 9 :(得分:0)

我通过设置变量来详细阐述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 '...'只会扩大变量的范围,而不需要export。 (见Setting an environment variable before a command in bash not working for second command in a pipe

答案 10 :(得分:0)

您可以使用此功能查找和搜索配置更改:

#!/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