当前位置:  开发笔记 > 编程语言 > 正文

我需要我的sed -i命令进行就地编辑才能使用GNU sed和BSD/OSX sed

如何解决《我需要我的sed-i命令进行就地编辑才能使用GNUsed和BSD/OSXsed》经验,为你挑选了6个好方法。

我有一个makefile(在Linux上为gmake开发),我试图移植到OSX,但似乎sed不想合作.我所做的是使用GCC自动生成依赖文件,然后使用sed稍微调整一下.makefile的相关部分:

$(OBJ_DIR)/%.d: $(SRC_DIR)/%.cpp
  $(CPPC) -MM -MD $< -o $@
  sed -i 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' $@

虽然这在GNU/Linux下没有遇到麻烦,但在尝试构建OSX时会出现如下错误:

sed: 1: "test/obj/equipmentConta ...": undefined label 'est/obj/equipmentContainer_utest.d'
sed: 1: "test/obj/dice_utest.d": undefined label 'est/obj/dice_utest.d'
sed: 1: "test/obj/color-string_u ...": undefined label 'est/obj/color-string_utest.d'

看起来sed正在砍掉一个角色,但我看不到解决方案.



1> martin clayt..:

OS Xsed-i不同于Linux版本的方式处理参数.

您可以通过-e以这种方式添加来生成可能"工作"的命令:

#      vv
sed -i -e 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' $@

OS X sed -i解释了-i作为就地编辑的备份副本的文件扩展名之后的下一件事.(只有-i在扩展名和扩展名之间没有空格时,Linux版本才会执行此操作.)显然,使用此版本的一个副作用是,您将获得一个备份文件-e作为扩展名,您可能不需要.有关更多详细信息,请参阅此问题的其他答案,以及可以使用的更清晰的方法.

您所看到的行为是因为OS X sed消耗s|||作为扩展名,然后解释(!),接下来的参数作为一个命令-在这种情况下,它开头t,其sed识别为一个分支到标签的命令预期目标标签作为参数-因此你看到的错误.

如果您创建文件test,则可以重现错误:

$ sed -i 's|x|y|' test
sed: 1: "test": undefined label 'est'


对OSX的正确修复是对`-i`有一个空参数,但是这又与Linux sed不兼容.) - :也许使用Perl代替?`perl -pi -e's | x | y | g'file`

2> Urkle..:

实际上......做

sed -i -e "s/blah/blah/" files

不会在OS X中执行您所期望的...而是创建具有"-e"扩展名的备份文件.

适合OS的是

sed -i "" -e "s/blah/blah/" files


我认为对于"工作"的慷慨定义就是这种情况.这是一个聪明的黑客,但它产生额外的文件.Urkle的答案正是我所寻找的.
是的,但是您的答案不适用于Linux,这是主要问题:两者都需要。使用-i -e,仍然可以删除-e扩展文件。

3> arctelix..:

目前接受的答案有两个非常重要的缺陷.

    使用BSD sed(OSX版本),该-e选项被解释为文件扩展名,因此创建具有扩展名的备份文件-e .

    所建议的对darwin内核的测试不是跨平台解决方案的可靠方法,因为GNU或BSD sed可能存在于任意数量的系统上.

一个更可靠的测试是简单地测试--version只在sed的GNU版本中找到的选项.

sed --version >/dev/null 2>&1

一旦确定了正确版本的sed,我们就可以用正确的语法执行命令.

-i选项的GNU sed语法:

sed -i -- "$@"

BSD sed -i选项的语法:

sed -i "" "$@"

最后把它们放在一个跨平台的函数中来执行一个就地编辑sed表示:

sedi () {
    sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@"
}

用法示例:

sedi 's/old/new/g' 'some_file.txt'

此解决方案已在OSX,Ubuntu,Freebsd,Cygwin,CentOS,Red Hat Enterprise和Msys上进行了测试.



4> Anthony Sott..:

这不是问题的答案,但可以通过以下方式获得与Linux等效的行为

brew install gnu-sed

# Add to .bashrc / .zshrc
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

(以前有一个--with-default-names选项,brew install gnu-sed但最近已被删除)


`brew install gnu-sed`会将`sed`安装为`gsed`,所以你不需要考虑用OSX的sed编写的前面脚本.
This is definitely the quickest solution for me.

5> thecarpy..:

我也遇到了这个问题并想到了以下解决方案:

darwin=false;
case "`uname`" in
  Darwin*) darwin=true ;;
esac

if $darwin; then
  sedi="/usr/bin/sed -i ''"
else
  sedi="sed -i"
fi

$sedi 's/foo/bar/' /home/foobar/bar

适合我;-),YMMV

我在一个多操作系统团队工作,其中ppl构建在Windows,Linux和OS X上.一些OS X用户抱怨因为他们有另一个错误 - 他们安装了sed的GNU端口,所以我必须指定完整路径.


您的解决方案是真正的跨平台。谢谢。

6> mklement0..:

马丁克莱顿的有用答案提供了一个很好的解释问题[1],但一个解决方案 - 正如他所说 - 具有潜在的不良副作用.

这是无副作用的解决方案:

警告:-i单独解决语法问题,如下所示,可能还不够,因为GNU sed和BSD/macOS 之间还有许多其他差异sed(有关综合讨论,请参阅我的这个答案).


解决方法-i:临时创建备份文件,然后进行清理:

使用非空后缀(备份文件文件扩展名)option-argument(不是空字符串的值),您可以使用-iBSD/macOS sed和GNU的方式sed,直接将后缀附加到-i选项.

这可用于暂时创建备份文件,您可以立即清理:

sed -i.bak 's/foo/bar/' file && rm file.bak

显然,如果你想保留备份,只需省略该&& rm file.bak部分即可.


与POSIX兼容的解决方法,使用临时文件和mv:

如果只需要就地编辑单个文件,-i可以绕过选项以避免不兼容.

如果将sed脚本和其他选项限制为符合POSIX的功能,则以下是完全可移植的解决方案(请注意,-i符合POSIX标准).

sed 's/foo/bar' file > /tmp/file.$$ && mv /tmp/file.$$ file

此命令只是将修改写入临时文件,如果sed命令成功(&&),则将原始文件替换为临时文件.

如果您确实希望将原始文件保留为备份,请添加另一个mv首先重命名原始文件的命令.

警告:从根本上说-i,除了它试图保留原始文件的权限和扩展属性(macOS)之外,这也是如此.但是,如果原始文件是符号链接,则此解决方案-i都将使用常规文件替换符号链接.
有关工作原理的详细信息,请参阅本答案的下半部分-i.


[1]有关更深入的解释,请参阅我的这个答案.

推荐阅读
赛亚兔备_393
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有