【问题标题】:Using Regex to replace or append line in file | Linux Shell script使用正则表达式替换或追加文件中的行 | Linux Shell 脚本
【发布时间】:2016-02-11 20:30:50
【问题描述】:

我正在尝试找到正确的命令来在一个看起来像这样的文件上执行:(即 .cshrc 文件)

setenv BLAH foo

我需要该命令来替换它检测到字符串BLAH 的行并像这样替换整行:

setenv BLAH newfoo 

如果文件中不存在BLAH,则将其附加到文件中。

我曾像这样玩过sed,但这并没有达到我的目标。

sed 's/^.*?BLAH.*/setenv BLAH newfoo/g' text.txt > text.tmp && mv text.tmp text.txt

我也玩过awk,但似乎也无法让命令完全按照我想要的方式工作。

awk -v s="setenv BLAH newfoo" '/^BLAH/{f=1;$0=s}7;END{if(!f)print s}' text.txt > text.tmp && mv text.tmp text.txt

任何关于如何实现这一点的想法都很棒。

更新

我正在努力使这个脚本按预期工作。

脚本

ARG1="$1"
sed -i "/BLAH/s/^.*\$/setenv ${ENV_KEY} ${ENV_VAL}/g" file.txt
# If the BLAH keyword isnt there, append the file then.
grep -v -q "BLAH" file.txt && echo "setenv BLAH ${ARG1}" >> file.txt

file.txt(默认)

setenv BLAH 随机字符串

脚本的使用

script.sh 新值

file.txt(结果)

setenv BLAH 新值

目前脚本似乎每次都在附加文件。我也尝试了其他一些建议,但我无法让他们接受 sed 字符串中传入的 arg1 值。

【问题讨论】:

    标签: regex linux shell awk sed


    【解决方案1】:

    简介

    这可以通过这个 脚本来完成:

    sed  '/BLAH/{s/ \w+$/ newfoo/;h};${x;/BLAH/ba;x;s/$/\nsetenv BLAH newfoo/;x;:a;x}'
    

    说明

    sed -e '
        /^\(\|.*\W\)BLAH\(\W.*\|\)$/{  # lines matching word BLAH
          s/ \w\+$/ newfoo/; # replace last word by "newfoo"
          h;         # Store for not adding them at end of file 
        };
        ${           # On last line...
          x;         # Swap stored line with current line
          /BLAH/ba;  # if match, branch to label a:
          x;         # Swap back
          s/$/\nsetenv BLAH newfoo/; # Replace end of line with newline...
          x;         # Swap again
        :a;          # Label a:
          x          # Swap back
    }'
    

    您可以使用 in place edition 开关:

    sed -e'/BLAH/{h};${x;/BLAH/ba;x;s/$/\nsetenv BLAH newfoo/;x;:a;x}' -i text.txt
    

    为了更准确,搜索分隔词BLAH:

    sed -e '
        /^\(.*\W\|\)BLAH\(\W.*\|\)$/{h}; # Store lines matching word BLAH
        ${           # On last line...
          x;         # Swap stored line with current line
          /./ba;  # if match, branch to label a:
          x;         # Swap back
          s/$/\nsetenv BLAH newfoo/; # Replace end of line with newline...
          x;         # Swap again
        :a;          # Label a:
          x          # Swap back
        }'  <(echo BLAH=foo;seq 1 4)
    BLAH=foo
    1
    2
    3
    4
    

    同时

    sed -e '
        /^\(.*\W\|\)BLAH\(\W.*\|\)$/{h}; # Store lines matching word BLAH
        ${           # On last line...
          x;         # Swap stored line with current line
          /./ba;  # if match, branch to label a:
          x;         # Swap back
          s/$/\nsetenv BLAH newfoo/; # Replace end of line with newline...
          x;         # Swap again
        :a;          # Label a:
          x          # Swap back
        }'  <(echo BLAHBLAH=foo;seq 1 4)
    BLAHBLAH=foo
    1
    2
    3
    4
    setenv BLAH newfoo
    

    您可能会看到第二个BLAH 被. 替换。可以这样做,因为如果没有找到 1st,则交换空间是空的。所以如果至少有一个字符,这意味着第一个确实发生了。

    清理和脚本

    有一种方法可以让这更脚本化:

    sedcmd='/^\(.*\W\|\)%s\(\W.*\|\)$/{s/ \w\+$/ %s/;h};'
    sedcmd+='${x;/./ba;x;s/$/\\nsetenv %s %s/;x;:a;x}'
    varnam=BLAH
    varcnt=foo
    filnam=/tmp/file.txt
    printf -v sedcmd "$sedcmd" ${varnam} ${varcnt} ${varnam} ${varcnt}
    sed -e "$sedcmd" -i "$filnam"
    

    您可以删除最后一行的-i 开关...

    你可以通过运行来试试这个:

    sed -e "$sedcmd" <(echo setenv BLAH oldfoo;seq 1 4) 
    setenv BLAH foo
    1
    2
    3
    4
    
    sed -e "$sedcmd" <(echo setenv BLAHBLAH oldfoo;seq 1 4) 
    setenv BLAHBLAH oldfoo
    1
    2
    3
    4
    setenv BLAH foo
    

    【讨论】:

    • 这非常有效。只需附加文件交换和宾果游戏。谢谢
    • 您可以使用 in place edition 开关 (-i) 逐步鞭打 文件交换
    • 完美,非常感谢
    • 如何在其中包含参数? newfoo=$1
    • 是的,如果您使用双引号 (") 而不是单引号 ('),您还必须转义 $ 符号和反斜杠。:sed -e "/^\\(.*\\W\\|\\)BLAH\\(\\W.*\\|\\)\$/{h};\${x;/./ba;x;s/$/\\nsetenv BLAH newfoo=\$1/;x;:a;x}" &lt;(echo BLAH=foo;seq 1 4)
    【解决方案2】:

    我认为你可以让sed 更容易一些,如果你之后快速传递grep 并在它仍然没有找到的情况下附加文本。

    例如

    ENV_KEY=BLAH
    ENV_VAL=foo
    sed -i "/BLAH/s/^.*\$/setenv ${ENV_KEY} ${ENV_VAL}/g" text.tmp
    ! grep -q "${ENV_KEY}" text.tmp &amp;&amp; echo "setenv ${ENV_KEY} ${ENV_VAL}" &gt;&gt; text.tmp

    这告诉sed 搜索字符串BLAH,如果找到则替换整行^.*$。 -i 选项告诉sed 更新文件。

    【讨论】:

    • 我应该期望 grep 每次都附加该行吗?似乎每次它都会不断添加新行,即使存在新行也是如此。两者都基于 val 更新。我需要有一个与我实际运行 sed 的文件不同的 tmp 文件吗?
    • 看看更新后的问题。我正在使用你的建议,但试图弄清楚如何让它按照你详细说明的方式工作。
    • 是的,我的错 - 我放了一个不适用于 -q 选项的grep -v。你只需要用! 否定shell 中的查询,我已经更新了我的答案。
    【解决方案3】:

    这可能对你有用(GNU sed):

    sed '/blah/h;//s/foo/newfoo/;$!b;G;/blah/!s/.$/&setenv blah newfoo/;t;P;d' file
    

    如果blah 发现将该行存储在保留空间 (HS) 中,并用 newfoo 替换 foo。如果不是文件结尾,则打印每一行。在文件末尾将 HS 附加到模式空间并检查 blah。如果找不到,则追加 setenv blah newfoo 并打印,否则打印最后一行并删除其余部分。

    【讨论】:

    • 感谢您的回答。
    【解决方案4】:

    这个awk 命令应该适用于这两种情况:

    awk -v kw='BLAH' '$2 == kw{$3="newfoo"; seen=1} 1;
          END{if (!seen) print "setenv " kw " newfoo"}' file
    

    您可以使用-v kw=... 命令行选项传递任何其他关键字进行搜索。

    END 块只有在文件中没有找到给定关键字时才会执行。

    【讨论】:

    • 我尝试使用它,但它不会替换整行,只会替换值。
    • awk -v kw='BLAH' '$2 == kw{$3=$HOME; seen=1} 1; END{if (!seen) print "setenv " kw " $HOME"}' $FILE &gt; test.tmp &amp;&amp; mv test.tmp $FILE
    • 如果您将setenv BLAH foo 改为setenv BLAH newfoo,那么您只需将第三个单词从foo 更改为newfoo
    • 确实如此。对不起。但是,如果假设文本值为 BLAH=asdfasdf 并用 export BLAH=newfoo 替换该行,这是否可行?
    • 你能举例说明如何更新值吗?我不断得到整条线。 ``` awk -v kw='BLAH' '$2 == kw{$3=newfoo;看到=1} 1; END{if (!seen) print "setenv " kw " newfoo"}' $FILE > .tmpf && mv .tmpf $FILE``` 输出setenv BLAH setenv BLAH newfoo
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-27
    • 2021-05-08
    • 2015-07-15
    • 2021-07-17
    • 1970-01-01
    • 2020-04-03
    • 2021-07-01
    相关资源
    最近更新 更多