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

2024-10-11 08:36:00
admin
原创
76
摘要:问题描述:我需要在配置文件的末尾添加以下行: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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用