如何使用sed正确执行此修改或插入行?

如何使用sed正确执行此修改或插入行?,sed,Sed,我在一个测试crontab文件上运行此命令,其中包含以下内容: 00 01 * * * foo --file example/example.xml --bla 00 02 * * * foo --file abc/abc.xml --bla 我正在尝试用其他内容替换第一行(如果存在),例如“bar”,或者如果它根本找不到它,则插入bar行,方法是运行以下命令: sed -e '@\(.*file example/example.xml .*\)@bar@' -e t -e 's@^@bar@

我在一个测试crontab文件上运行此命令,其中包含以下内容:

00 01 * * * foo --file example/example.xml --bla
00 02 * * * foo --file abc/abc.xml --bla
我正在尝试用其他内容替换第一行(如果存在),例如“bar”,或者如果它根本找不到它,则插入bar行,方法是运行以下命令:

sed -e '@\(.*file example/example.xml .*\)@bar@' -e t -e 's@^@bar@' file
我希望上面的代码尝试用bar替换匹配的示例行,如果找不到,则插入bar行。但是,当我使用上面的代码运行它时,我得到以下输出:

bar
bar00 02 * * * foo --file abc/abc.xml --bla
因此,它执行了替换,但不知何故,
t
跳转到末尾没有发生,它还在下一行插入了
bar
。出了什么问题

下面sat给出的命令似乎有效,但如果我运行两次,就无法追加,我不确定原因:

cat file
1 bin/appple #apple
2 bin/orange #orange
cat file | sed -e '\@.* #apple@{d}; $a 1 bin/apple #apple'
2 bin/orange #orange
1 bin/apple #apple
cat file | sed -e '\@.* #apple@{d}; $a 1 bin/apple #apple' | sed -e '\@.* #apple@{d}; $a 1 bin/apple #apple'
2 bin/orange #orange

前面两次,上面的命令都是我想要的,但最后一个命令只是删除了该行,而没有将其追加回去。为什么会这样?(顺便说一句,我把#改为\@是因为我用了#在那里,你可以想象它是一个crontab注释)

你可以试试这个
sed

sed '\#file example/example.xml#{d}; $a bar' yourfile
修复打字错误(缺少的
s
)后,命令:

sed -e 's@\(.*file example/example.xml .*\)@bar@' -e t -e 's@^@bar@' file
正在按照您的要求工作;我认为你误解了
t
的范围。该测试依次应用于每一行输入,而不是整个脚本。剧本上说:

  • 如果该行包含
    文件example/example.xml
    ,请将其替换为
  • 如果替换完成,请跳到脚本的末尾(对于当前输入行)
  • 将行的开头替换为
    (第二个数据行会发生这种情况,因为它与
    文件example/example.xml
    不匹配)
(我顺便指出,当替换为
条形时,不需要
\(
\)
,但这可能只是使用
\1
的更复杂替换的残余)

嗯,;这就是为什么脚本的行为如此。我们怎样才能让它按你想要的方式运行?在某种程度上,答案取决于输出中行的顺序是否至关重要

  • 如果顺序不重要,那么由by来完成工作。如果找到匹配的文本行,则删除该行。在文件末尾添加新行。因此,无论行是否在那里,输出都会在文件末尾包含新的必需输出。(对于
    crontab
    文件,这应该足够了。)

  • 如果顺序真的很重要,你的工作就要困难得多。在这种情况下,您可以利用保持空间来确定是否找到了模式。但是,如果您需要为其他内容保留空间,或者您需要替换或添加多个模式,那么您无法轻松地调整此模式以解决问题(您必须更加聪明)

  • 如果命令重要的话 如果行的顺序不重要,不要使用此选项

    示例:文件
    sed.script
    包含:

    /.*file example\/example.xml.*/ {
    h
    s//bar/
    }
    $ {
    p
    x
    s/./X/
    t end
    s/.*/bar/
    q
    :end
    d
    }
    
    此脚本有两组操作。第一组,当行与
    文件example/example.xml
    匹配时是简单的;它将该行复制到保留空间(因此保留空间不再为空),然后执行替换操作

    第二组要复杂得多;它在脚本末尾运行。首先,它打印当前行。如果第一个块修复了该行(因此,
    文件example/example.xml
    出现在最后一行),它将打印该行的修改形式。然后它交换保持空间和模式空间。它进行了一次试替换(
    s//X/
    )。如果进行了替换,则
    t
    命令将跳转到标签
    end
    ,这意味着该行不是空的,这意味着保留空间已填充,因此该图案在替换之前(至少)被替换一次。如果
    t
    没有跳转,则看不到匹配模式,因此代码将用
    bar
    (替换文本)替换当前行并退出(打印最后一行并放弃脚本)。标签
    end
    后面紧跟着
    d
    ,它删除当前行并“移动到下一个周期”,除非当
    $
    匹配生效时没有下一个周期,因此没有任何内容可打印

    示例运行:

    $ cat file
    00 01 * * * foo --file example/example.xml --bla
    00 02 * * * foo --file abc/abc.xml --bla
    $ sed -f sed.script file
    bar
    00 02 * * * foo --file abc/abc.xml --bla
    $ sed -f sed.script file | sed -f sed.script
    bar
    00 02 * * * foo --file abc/abc.xml --bla
    bar
    $ sed -f sed.script /dev/null
    $
    
    第一个
    sed
    显示
    example/example.xml
    行被原位替换。第二个
    sed
    显示当缺少
    example/example.xml
    行时,会在末尾添加
    bar
    。最后一个示例(
    /dev/null
    )显示,您至少需要一行输入,它才能正常工作;它不会从空文件引导

    可能有一种更简单的方法来处理这个问题。我只是还没弄清楚是什么

    如果顺序不重要的话 这基本上是操作顺序颠倒的结果。当要删除的行是文件中的最后一行时,该选项起作用:

    sed -e '$a bar' -e '/.*file example\/example.xml.*/d' file
    

    这是干什么用的?一美元?这是在做什么?
    #
    是一个模式匹配分隔符,
    $
    是文件的结尾,
    是一个
    是附加。我想你应该解释一下你在做什么。它是有效的,但不清楚它是如何工作的或为什么工作的,因为您已经重写了算法(它没有完全按照要求执行,但结果是等效的)您使用的是
    sed
    的一个比较模糊的部分,带有
    \\\\
    符号。正如您所指出的,它的行为与预期的一样,但当我运行两次时,它实际上执行的是删除,而不是追加。出于某些原因,您可以通过使用
    sed-e'$a bar'-e'/.*文件示例\/example.xml.*/d'
    (简单地反转追加和删除操作)。第二个示例非常复杂,由于某种原因,@sat-give的简单命令在迭代两次时不起作用-正如我在上面的示例中所做的编辑一样。当最后一行匹配时,变量by可能会失败,因为
    d
    终止当前代码