【问题标题】:Redirect output from sed 's/c/d/' myFile to myFile将 sed 's/c/d/' myFile 的输出重定向到 myFile
【发布时间】:2011-02-04 20:09:02
【问题描述】:

我在脚本中使用 sed 进行替换,我想让替换的文件覆盖文件。通常我认为你会使用这个:

% sed -i 's/cat/dog/' manipulate
sed: illegal option -- i

但是,正如您所见,我的 sed 没有该命令。

我试过了:

% sed 's/cat/dog/' manipulate > manipulate

但这只是将操作变成一个空文件(有道理)。

这行得通:

% sed 's/cat/dog/' manipulate > tmp; mv tmp manipulate

但我想知道是否有一种标准方法可以将输出重定向到与输入相同的文件中。

【问题讨论】:

  • 怎么没人提到sponge
  • 我猜问题不是sponge-worthy
  • 2020 年的电话:为什么sed 's/cat/dog/' manipulate > manipulate 会创建一个空文件?

标签: bash sed solaris redirect


【解决方案1】:

我通常使用第 3 种方式,但有一个重要的变化

$ sed 's/cat/dog/' manipulate > tmp && mv tmp manipulate

即将; 更改为&&,这样移动只有在sed 成功时才会发生;否则,一旦您在 sed 语法中出现拼写错误,您就会丢失原始文件。

注意!对于那些阅读标题并缺少 OP 约束的人“我的 sed 不支持 -i:对于大多数人来说,sed 将支持 @987654325 @,所以最好的方法是:

$ sed -i 's/cat/dog/' manipulate

【讨论】:

    【解决方案2】:

    是的,FreeBSD/MacOSX sed 也支持-i,但需要空字符串作为参数来就地编辑文件。

    sed -i "" 's/old/new/g' file   # FreeBSD sed
    

    【讨论】:

    • -i 不需要空字符串。参数的值是要使用的备份扩展,当它是空字符串时 - 不会进行备份。在 GNU sed 上,-i 的默认值为空字符串。
    • 这是我的救命稻草
    【解决方案3】:

    如果您不想移动副本,可以使用 ed:

    ed file.txt <<EOF
    %s/cat/dog/
    wq
    EOF
    

    【讨论】:

    • ed 是否支持 sed 支持的所有正则表达式?具体范围?
    • ed 支持 sed 支持的所有正则表达式,包括范围。 ed - 适用于文件 sed - 适用于流 另一个主要区别是 ed 命令默认仅适用于文件的当前行(这就是我编辑示例并添加 % 以使命令运行的原因在所有行上),而 sed 命令在默认情况下针对所有行运行。
    • 你也可以使用 "ex",它是 "vim" 的别名。它的功能非常丰富。
    【解决方案4】:

    Kernighan 和 Pike 在Unix 编程的艺术 中讨论了这个问题。他们的解决方案是编写一个名为overwrite 的脚本,它允许人们做这样的事情。

    用法是:overwrite file cmd file

    # overwrite: copy standard input to output after EOF
    
    opath=$PATH
    PATH=/bin:/usr/bin
    
    case $# in
    0|1)   echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2
    esac
    
    file=$1; shift
    new=/tmp/overwr1.$$; old=/tmp/overwr2.$$
    trap 'rm -f $new $old; exit 1' 1 2 15  # clean up
    
    if PATH=$opath "$@" >$new
    then
           cp $file $old           # save original
           trap '' 1 2 15          # wr are commmitted
           cp $new $file
    else
           echo "overwrite: $1 failed, $file unchanged" 1>&2
           exit 1
    fi
    rm -f $new $old
    

    在您的$PATH 中拥有上述脚本后,您可以执行以下操作:

    overwrite manipulate sed 's/cat/dog/' manipulate
    

    为了让您的生活更轻松,您可以使用同一本书中的replace 脚本:

    # replace: replace  str1 in files with str2 in place
    PATH=/bin:/usr/bin
    
    case $# in
        0|2) echo 'Usage: replace str1 str2 files' 1>&2; exit 1
    esac
    
    left="$1"; right="$2"; shift; shift
    
    for i
    do
        overwrite $i sed "s@$left@$right@g" $i
    done
    

    在您的$PATH 中添加replace 也可以让您说:

    replace cat dog manipulate
    

    【讨论】:

    • 或者在这种情况下,只需从 www.sunfreeware.com 下载符合 -i 选项的新版本 sed ;)
    • +1,覆盖脚本非常好用(根据个人喜好改了几处之后)!
    • @Anders:用非标准替代品替换系统 sed,无论它多么免费,可能不是一个可行的选择,也没有回答如何使用可用/标准来做到这一点的问题工具。
    • Solaris 遵循的 POSIX 标准仅规定了 sed 兼容的最小选项集,因此 GNU/Sed 或您喜欢的任何名称实际上都符合您所谓的“标准工具” .
    • @Anders,它与 POSIX 兼容,但如果您使用非 POSIX 选项,那么您当然不依赖 标准 工具。一个人当然可以在系统上安装 GNU sed,但后来可能就做不到了。
    【解决方案5】:

    您可以使用moreutils 中的sponge

    sed "s/cat/dog/" manipulate | sponge manipulate
    

    【讨论】:

    • 对我来说,这是最好的答案
    【解决方案6】:

    也许-i 是 gnu sed,或者只是 sed 的旧版本,但无论如何。你在正确的轨道上。第一个选项可能是最常见的选项,第三个选项是如果您希望它在任何地方(包括 solaris 机器)都可以工作...... :) 这些是“标准”方式。

    【讨论】:

    • -i 也支持 FreeBSD/MacOSX sed
    【解决方案7】:

    要更改多个文件(并将每个文件的备份保存为 *.bak):

    perl -p -i -e "s/oldtext/newtext/g" *

    replaces any occurence of oldtext by newtext in all files in the current folder. However you will have to escape all perl special characters within oldtext and newtext using the backslash 
    
    This is called a “Perl pie” (mnemonic: easy as a pie)
    The -i flag tells it do do in-place replacement, and it should be ok to use single (“'”) as well as double (“””) quotes.
    
        If using ./* instead of just *, you should be able to do it in all sub-directories 
    See man perlrun for more details, including how to take a backup file of the original.
    using sed:
                sed -i    's/old/new/g' ./*  (used in GNU)
        sed -i '' 's/old/new/g' ./*  (used in FreeBSD)
    

    【讨论】:

      【解决方案8】:

      -i 选项在标准sed 中不可用。

      您的替代方案是您的第三种方式或perl

      【讨论】:

        【解决方案9】:

        很多答案,但没有一个是正确的。这是正确和最简单的一个:

        $ echo "111 222 333" > file.txt
        $ sed -i -s s/222/444/ file.txt 
        $ cat file.txt
        111 444 333
        $ 
        

        【讨论】:

        • OP 明确指出-i 标志被他的sed 版本视为“非法”。所以不能用...
        【解决方案10】:

        使用打开文件句柄的解决方法:

        exec 3<manipulate 
        

        防止打开的文件被截断:

        rm manipulate
        sed 's/cat/dog/' <&3 > manipulate
        

        【讨论】:

        • 虽然在技术上是正确的并且是一个可爱的技巧,但这是危险。如果在 sed 执行后您的电源发生故障,您最终会丢失数据。
        • 在删除之前链接到其他地方就可以了。
        猜你喜欢
        • 1970-01-01
        • 2016-06-11
        • 2019-03-21
        • 2022-08-07
        • 1970-01-01
        • 2013-09-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多