仅当文件尚不存在时才将行附加到文件

2024-10-11 08:36:00
admin
原创
97
摘要:问题描述:我需要在配置文件的末尾添加以下行:include "/configs/projectname.conf" 到名为lighttpd.conf我正在研究如何使用它sed来做到这一点,但我不知道如何做。如果该行尚不存在,我该如何插入它?解决方案 1:保持简单就好:)gr...

问题描述:

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

include "/configs/projectname.conf"

到名为lighttpd.conf

我正在研究如何使用它sed来做到这一点,但我不知道如何做。

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


解决方案 1:

保持简单就好:)

grep + echo就足够了:

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

编辑:合并@cerin 和@thijs-wouters 的建议

解决方案 2:

这将是一个干净、可读且可重复使用的解决方案grep,使用echo并仅在文件不存在时才向文件添加一行:

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

如果需要匹配整条线路,请使用grep -xqF

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

解决方案 3:

尝试一下:

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

解决方案 4:

使用 sed,最简单的语法:

sed \n    -e '/^(option=).*/{s//value/;:a;n;ba;q}' \n    -e '$aoption=value' filename

如果参数存在,这将替换它,否则将其添加到文件底部。

-i如果您想就地编辑文件,请使用该选项。


如果您想要接受并保留空格,并且除了删除注释之外,如果该行已经存在,但被注释掉,请写下:

sed -i \n    -e '/^#?(s*options*=s*).*/{s//value/;:a;n;ba;q}' \n    -e '$aoption=value' filename

请注意,选项和值都不能包含斜线/,否则您必须将其转义为/


要使用 bash-variables$option$value,你可以写:

sed -i \n    -e '/^#?(s*'${option////\/}'s*=s*).*/{s//'${value////\/}'/;:a;n;ba;q}' \n    -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 \n        -e '/^#?(s*'"${name}"'s*=s*).*/{s//'"${value}"'/;:a;n;ba;q}' \n        -e '$a'"${name}"'='"${value}" $3
}

解释:

  • /^(option=).*/`option=:匹配以和 ( )开头的行,.*忽略 之后的所有内容=(...)括起我们稍后将重复使用的部分`。

  • #/^#?(s'"${option//////}"'s=s)./:忽略行首带有的注释掉的代码。?表示“可选”。注释将被删除,因为它位于(...中复制的部分之外)s*表示“任意数量的空格”(空格、制表符)。空格会被复制,因为它们位于(...内),因此您不会丢失格式。

  • /^(option=).*/{…}:如果匹配一行/…/,则执行下一条命令。执行的命令不是单个命令,而是一个块{…}

  • s//…/:搜索并替换。由于搜索词为空//,因此它适用于最后一个匹配项,即/^(option=).*/

  • s//value/: Replace the last match with everything in (…), referenced by 1and the text值`

  • :a;n;ba;q:设置标签a,然后读取下一行n,然后分支b(或转到)回到标签a,这意味着:读取所有行直到文件末尾,因此在第一个匹配之后,只需获取所有后续行而无需进一步处理。然后q退出,因此忽略其他所有内容。

  • $aoption=value:在文件末尾$附加a文本option=value

有关更多信息sed和命令概述请参阅我的博客:

  • https://marc.wäckerlin.ch/computer/stream-editor-sed-overview-and-reference

解决方案 5:

如果写入受保护的文件,@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:

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

解决方案 7:

如果有一天,其他人必须将此代码作为“遗留代码”来处理,那么如果您编写一个不那么深奥的代码,那个人会很感激,例如

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

解决方案 8:

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

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

“正确转义”意味着放置一个与您的输入匹配的正则表达式,即从您的实际输入中转义所有正则表达式控制,即在 ^$/*?+() 前面放置一个反斜杠。

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

解决方案 9:

这是一个单行 sed,它可以内联完成这项工作。请注意,当变量存在时,它会保留变量的位置及其在文件中的缩进。这对于上下文通常很重要,例如当周围有注释或变量位于缩进块中时。任何基于“删除然后追加”范式的解决方案在这方面都失败了。

   sed -i '/^[     ]*option=/{h;s/=.*/=value/};${x;/^$/{s//option=value/;H};x}' test.conf

使用通用的变量/值对,你可以这样写:

   var=c
   val='12 34' # it handles spaces nicely btw
   sed -i '/^[     ]*'"$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 '/^[     ]*'"$var"'=/{h;s/=[^#]*(.*)/='"$val"'/;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

解决方案 10:

作为 awk-only 单行命令:

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`ENDfileENDspongemv`file

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

为了便于阅读,我添加了一些空格(不是很多),但整个 awk 程序中实际上只需要一个空格,即 后面的空格print。如果file包含# comments它们,它们将被保留。

解决方案 11:

这是一个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 && \n   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

解决方案 12:

使用 awk

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

解决方案 13:

 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

解决方案 14:

以下是 awk 的一行代码:

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

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

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

解决方案 15:

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

解决方案 16:

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

将缓冲区中的所有文件加载到文件中,最后更改所有出现的项,如果没有发生更改,则将其添加到末尾

解决方案 17:

使用 grep 的答案是错误的。您需要添加 -x 选项来匹配整行,否则#text to add当试图精确添加时,像 这样的行仍然会匹配text to add

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

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

解决方案 18:

根据我的经验,几乎所有答案都有效,但并非在所有场景或操作系统中都有效。唯一适用于旧系统和新操作系统和不同操作系统的是以下内容。

如果不存在 KUBECONFIG 路径,我需要将其附加到 bashrc 文件中。所以我做的是

我假设它存在并将其删除。

使用 sed 我附加我想要的字符串。

sed -i '/KUBECONFIG=/d' ~/.bashrc

echo 'export KUBECONFIG=/etc/rancher/rke2/rke2.yaml' >> ~/.bashrc

解决方案 19:

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

解决方案 20:

尝试:

LINE='include "/configs/projectname.conf"'
sed -n "|$LINE|q;$a $LINE" lighttpd.conf >> lighttpd.conf

使用管道作为分隔符,如果$LINE找到则退出。否则,附加$LINE到末尾。

因为我们只在 sed 命令中读取文件,所以我认为我们总体上没有 clobber 问题(这取决于您的 shell 设置)。

解决方案 21:

我建议仅使用sed以下解决方案:

sed -i \n    -e 's#^include "/configs/projectname.conf"#include "/configs/projectname.conf"#' \n    -e t \n    -e '$ainclude "/configs/projectname.conf"' lighttpd.conf

s`include "/configs/projectname.conf用其自身替换该行(#`此处用作分隔符)

t如果替换成功,则跳过其余命令

$a否则跳到最后一行并附加到include "/configs/projectname.conf其后

解决方案 22:

我详细阐述了 kev 的 grep/sed 解决方案,通过设置变量来减少重复。

在第一行设置变量(提示:$_option应匹配该行上的所有内容直到值[包括任何分隔符,如 = 或 :])。

_file="/etc/ssmtp/ssmtp.conf" _option="mailhub=" _value="my.domain.tld" \n        sh-c'\n            grep -q "^$_option" "$_file" \n            && sed -i "s/^$_option.*/$_option$_value/" "$_file" \n            || 回显“$_option$_value”>>“$_file”\n        ‘

请注意,sh -c '...'just 具有扩大变量范围的效果,而无需export。 (请参阅在 bash 中命令前设置环境变量对管道中的第二个命令不起作用)

解决方案 23:

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

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

解决方案 24:

如果您想在 Linux 终端中使用python 脚本运行此命令...

import os,sys
LINE = 'include '+ <insert_line_STRING>
FILE = <insert_file_path_STRING>                                
os.system('grep -qxF $"'+LINE+'" '+FILE+' || echo $"'+LINE+'" >> '+FILE)

$ 和双引号让我陷入了困境,但这个方法有效。谢谢大家

解决方案 25:

我需要编辑具有受限写权限的文件,因此需要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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   681  
  在项目管理领域,集成产品开发(IPD)流程以其高效、协同的特点,被众多企业视为提升产品竞争力的关键。IPD流程强调跨部门、跨职能的紧密合作,以确保产品从概念到市场各个环节的无缝衔接。然而,实现这一目标并非易事,它需要企业深刻理解并掌握IPD流程中的跨部门协作艺术。本文将深入探讨IPD流程中跨部门协作的三个关键点,旨在为...
IPD项目管理咨询   9  
  掌握IPD流程图:提升团队协作的关键路径在当今快速变化的商业环境中,团队协作的效率与效果直接关系到项目的成功与否。集成产品开发(Integrated Product Development,简称IPD)作为一种先进的研发管理理念,通过跨部门、跨领域的协同工作,能够显著提升产品开发的速度与质量。而IPD流程图,则是这一理...
IPD流程阶段   9  
  IPD流程概述:理解其核心价值与实施背景集成产品开发(Integrated Product Development,简称IPD)是一种先进的产品开发管理理念,它强调跨部门协作、市场导向和快速响应变化的能力。IPD流程不仅关注产品本身的技术创新,更注重将市场、研发、生产、销售等各个环节紧密集成,以实现产品从概念到市场的高...
华为IPD是什么   7  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程以其跨部门协作、高效决策和快速响应市场变化的特点,被众多企业视为提升竞争力的关键。然而,实践IPD流程并非易事,项目管理中的种种错误往往阻碍了其效果的充分发挥。本文旨在深入探讨如何在实施IPD流程时避免这些常见错误,...
IPD框架   7  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用