macos 用于就地编辑的 sed -i 命令,用于 GNU sed 和 BSD/OSX
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2320564/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
sed -i command for in-place editing to work with both GNU sed and BSD/OSX
提问by Chris Tonkinson
I've got a makefile (developed for gmake
on Linux) that I'm attempting to port to MacOS, but it seems like sed
doesn't want to cooperate. What I do is use GCC
to autogenerate dependency files, and then tweak them a bit using sed
. The relevant portion of the makefile
:
我有一个 makefile(gmake
在 Linux 上开发),我试图将它移植到 MacOS,但似乎sed
不想合作。我所做的是用于GCC
自动生成依赖文件,然后使用sed
. 的相关部分makefile
:
$(OBJ_DIR)/%.d: $(SRC_DIR)/%.cpp
$(CPPC) -MM -MD $< -o $@
sed -i 's|\(.*\)\.o:|$(OBJ_DIR)/.o $(OBJ_DIR)/.d $(TEST_OBJ_DIR)/_utest.o:|' $@
While this runs with no trouble under GNU/Linux, I get errors like the following when attempting to build on MacOS:
虽然这在 GNU/Linux 下运行没有问题,但在尝试在 MacOS 上构建时我会收到如下错误:
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'
It would seem like sed
is chopping off a character, but I can't see the solution.
看起来像是sed
砍掉了一个角色,但我看不到解决方案。
回答by martin clayton
OS X sed
handles the -i
argument differently to the Linuxversion.
You can generate a command that might "work" for both by adding -e
in this way:
您可以通过-e
以这种方式添加来生成一个可能对两者都“有效”的命令:
# vv
sed -i -e 's|\(.*\)\.o:|$(OBJ_DIR)/.o $(OBJ_DIR)/.d $(TEST_OBJ_DIR)/_utest.o:|' $@
OS X sed -i
interprets the next thing after the -i
as a file extension for a backup copyof the in-place edit. (The Linux version only does this if there is no space between the -i
and the extension.) Obviously a side affect of using this is that you will get a backup file with -e
as an extension, which you may not want.Please refer to other answers to this question for more details, and cleaner approaches that can be used instead.
OS Xsed -i
将 之后的下一个内容解释-i
为就地编辑的备份副本的文件扩展名。(Linux 版本仅-i
在扩展名和扩展名之间没有空格时才这样做。)显然,使用它的副作用是您将获得一个-e
作为扩展名的备份文件,而您可能不想要它。请参阅此问题的其他答案以获取更多详细信息,以及可以使用的更简洁的方法。
The behaviour you see is because OS X sed
consumes the s|||
as the extension (!) then interprets the nextargument as a command - in this case it begins with t
, which sed
recognizes as a branch-to-label command expecting the target label as an argument - hence the error you see.
您看到的行为是因为 OS Xsed
将s|||
用作扩展名 (!) 然后将下一个参数解释为命令 - 在这种情况下,它以 开头t
,它sed
识别为期望目标标签作为参数的分支到标签命令 -因此你看到的错误。
If you create a file test
you can reproduce the error:
如果您创建一个文件,test
您可以重现该错误:
$ sed -i 's|x|y|' test
sed: 1: "test": undefined label 'est'
回答by Urkle
Actually, doing
其实,做
sed -i -e "s/blah/blah/" files
doesn't do what you expect in MacOS either. Instead it creates backup files with -e
extension.
也不会在 MacOS 中执行您期望的操作。相反,它会创建带有-e
扩展名的备份文件。
The proper command for MacOS is
MacOS 的正确命令是
sed -i "" -e "s/blah/blah/" files
On Linux, remove the space between -i
and ""
(see related answer)
在Linux上,除去之间的空间-i
和""
(见相关答案)
sed -i"" -e "s/blah/blah/" files
回答by arctelix
The currently accepted answer is flawed in two very important ways.
当前接受的答案在两个非常重要的方面存在缺陷。
With BSD sed (the OSX version), the
-e
option is interpreted as a file extension and therefore creates a backup file with a-e
extension.Testing for the darwin kernel as suggested is not a reliable approach to a cross platform solution since GNU or BSD sed could be present on any number of systems.
对于 BSD sed(OSX 版本),该
-e
选项被解释为文件扩展名,因此会创建一个带有-e
扩展名的备份文件。按照建议测试 darwin 内核并不是跨平台解决方案的可靠方法,因为 GNU 或 BSD sed 可能存在于任意数量的系统上。
A much more reliable test would be to simply test for the --version
option which is only found in the GNU version of sed.
一个更可靠的测试是简单地测试--version
仅在 sed 的 GNU 版本中找到的选项。
sed --version >/dev/null 2>&1
Once the correct version of sed is determined, we can then execute the command in its proper syntax.
一旦确定了 sed 的正确版本,我们就可以以其正确的语法执行命令。
GNU sed syntax for -i option:
-i 选项的 GNU sed 语法:
sed -i -- "$@"
BSD sed syntax for -i option:
-i 选项的 BSD sed 语法:
sed -i "" "$@"
Finally put it all together in a cross platform function to execute an in place edit sed commend:
最后将它们放在一个跨平台函数中以执行就地编辑 sed 命令:
sedi () {
sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@"
}
Example usage:
用法示例:
sedi 's/old/new/g' 'some_file.txt'
This solution has been tested on OSX, Ubuntu, Freebsd, Cygwin, CentOS, Red Hat Enterprise, & Msys.
此解决方案已在 OSX、Ubuntu、Freebsd、Cygwin、CentOS、Red Hat Enterprise 和 Msys 上进行了测试。
回答by Anthony Sottile
This isn't quite an answer to the question, but one can get linux-equivalent behavior through
这不是对问题的完全答案,但是可以通过以下方式获得与 linux 等效的行为
brew install gnu-sed
# Add to .bashrc / .zshrc
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
(previously there was a --with-default-names
option to brew install gnu-sed
but that has recently been removed)
(以前有一个--with-default-names
选项,brew install gnu-sed
但最近已被删除)
回答by mklement0
martin clayton's helpful answerprovides a good explanation of the problem[1], but a solution that - as he states - has a potentially unwanted side effect.
马丁克莱顿的有用答案很好地解释了问题[1],但解决方案 - 正如他所说 - 具有潜在的不需要的副作用。
Here are side-effect-free solutions:
以下是无副作用的解决方案:
Caveat: Solving the -i
syntax problem alone, as below, may not be enough, because there are many other differences between GNU sed
and BSD/macOS sed
(for a comprehensive discussion, see this answerof mine).
警告:-i
单独解决语法问题,如下所示,可能还不够,因为 GNUsed
和 BSD/macOS之间还有许多其他差异sed
(有关全面的讨论,请参阅我的这个答案)。
Workaround with -i
: Create a backup file temporarily, then clean it up:
解决方法-i
:临时创建一个备份文件,然后清理它:
With a non-emptysuffix (backup-file filename extension) option-argument (a value that is not the empty string), you canuse -i
in a way that works with both BSD/macOS sed
and GNU sed
, by directly appending the suffix to the -i
option.
随着非空的后缀(备份文件扩展名)选项参数(这是一个值不是空字符串),你可以使用-i
的方式,既BSD / MacOS的作品sed
和GNU sed
,通过后缀直接附加到-i
选项。
This can be utilized to create a backup file temporarilythat you can clean up right away:
这可以用来临时创建一个备份文件,您可以立即清理:
sed -i.bak 's/foo/bar/' file && rm file.bak
Obviously, if you do want to keep the backup, simply omit the && rm file.bak
part.
显然,如果您确实想保留备份,只需省略该&& rm file.bak
部分即可。
Workaround that is POSIX-compliant, using a temporary file and mv
:
符合 POSIX 的解决方法,使用临时文件和mv
:
If only a singlefile is to be edited in-place, the -i
option can be bypassedto avoid the incompatibility.
如果只对单个文件进行就地编辑,则-i
可以绕过该选项以避免不兼容。
If you restrict your sed
script and other options to POSIX-compliant features, the following is a fully portablesolution (note that -i
is notPOSIX-compliant).
如果你限制你的sed
脚本和其他选项,以符合POSIX标准的功能,下面是一个完全便携式的解决方案(注意,-i
是不符合POSIX标准)。
sed 's/foo/bar' file > /tmp/file.$$ && mv /tmp/file.$$ file
This command simply writes the modifications to a temporary file and, if the
sed
command succeeds (&&
), replaces the original file with the temporary one.- If you do want to keep the original file as a backup, add another
mv
command that renames the original first.
- If you do want to keep the original file as a backup, add another
Caveat: Fundamentally, this is what
-i
does too, except that it tries to preserve permissions and extended attributes (macOS) of the original file; however, if the original file is a symlink, both this solution and-i
will replace the symlink with a regular file.
See the bottom half of this answerof mine for details on how-i
works.
此命令只是将修改写入临时文件,如果
sed
命令成功 (&&
),则将原始文件替换为临时文件。- 如果您确实想保留原始文件作为备份,请添加另一个
mv
首先重命名原始文件的命令。
- 如果您确实想保留原始文件作为备份,请添加另一个
警告:从根本上说,这也是
-i
如此,只是它试图保留原始文件的权限和扩展属性 (macOS);但是,如果原始文件是symlink,则此解决方案和-i
将用常规文件替换符号链接。
有关工作原理的详细信息,请参阅我的这个答案的下半部分-i
。
[1] For a more in-depth explanation, see this answerof mine.
[1] 有关更深入的解释,请参阅我的这个答案。
回答by thecarpy
I came across this issue as well and thought of the following solution:
我也遇到了这个问题,并想到了以下解决方案:
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
Works for me ;-), YMMV
对我有用 ;-), YMMV
I work in a multi-OS team where ppl build on Windows, Linux and OS X. Some OS X users complained because they got another error - they had the GNU port of sed installed so I had to specify the full path.
我在一个多操作系统团队工作,其中 ppl 在 Windows、Linux 和 OS X 上构建。一些 OS X 用户抱怨,因为他们遇到了另一个错误 - 他们安装了 sed 的 GNU 端口,所以我必须指定完整路径。
回答by niieani
I've corrected the solution posted by @thecarpy:
我已经更正了@thecarpy 发布的解决方案:
Here's a proper cross-platform solution for sed -i
:
这是一个合适的跨平台解决方案sed -i
:
sedi() {
case $(uname) in
Darwin*) sedi=('-i' '') ;;
*) sedi='-i' ;;
esac
LC_ALL=C sed "${sedi[@]}" "$@"
}