我试图找出是否有可能在单个sed命令中编辑文件,而无需手动将编辑后的内容流式传输到新文件中,然后将新文件重命名为原始文件名。我尝试使用-i选项,但是我的Solaris系统说-i是非法选项。有其他方法吗?

评论

-i是gnu sed中的选项,但不是标准sed中的选项。但是,它将内容流式传输到新文件,然后重命名该文件,因此它不是您想要的。

实际上,这就是我想要的,我只是不想暴露于必须执行将新文件重命名为原始名称的普通任务

然后您需要重新陈述问题。

@amphibient:您是否愿意在问题的标题前加上“ Solaris”一词?您的问题的价值正在丢失。请查看我的答案下方的评论。谢谢。

@Steve:我再次从标题中删除了Solaris前缀,因为它绝不是Solaris专有的。

#1 楼

-i选项将编辑后的内容流式传输到一个新文件中,然后在后台将其重命名。

示例:

sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename




sed -i '' 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename


在macOS上。

评论


至少它对我有用,所以我不必

–两栖动物
2012年10月2日在18:47

然后,您可以尝试在系统上编译GNU sed。

–乔罗巴
2012年10月2日在18:48

例如:sed -i“ s / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g” <文件>

– Thales Ceolin
2014年4月17日13:30



这比剥离溶液更好。不知何故,它不会创建任何.swap文件....谢谢!

–数学
14年7月3日,0:51

@maths:是的,但是也许在其他地方或更短的时间?

–乔罗巴
2014年7月3日在5:38

#2 楼

sed无法编辑文件的系统上,我认为更好的解决方案是使用perl

perl -pi -e 's/foo/bar/g' file.txt


尽管这样做确实会创建临时文件文件,它将替换原始文件,因为提供了空的后缀/扩展名。

评论


它不仅会创建一个临时文件,还会断开硬链接(例如,代替更改文件的内容,而是使用相同名称的新文件替换该文件。)有时这是所需的行为,有时是可以接受,但是当有多个链接指向文件时,几乎总是错误的行为。

–威廉·珀塞尔(William Pursell)
2012年10月3日,11:37

@DwightSpencer:我相信所有没有Perl的系统都坏了。当一个命令需要提升的权限并且必须使用sudo时,单个命令比一连串命令要重要。在我看来,问题在于如何避免手动创建和重命名临时文件而不必安装最新的GNU或BSD sed。只需使用Perl。

–史蒂夫
2014年2月20日下午0:42

@PetrPeller:真的吗?如果仔细阅读了该问题,您将理解OP会尝试避免使用sed's / foo / bar / g'file.txt> file.tmp && mv file.tmp file.txt之类的东西。仅仅因为就地编辑使用临时文件进行重命名,并不意味着他/她在选项不可用时必须执行手动重命名。还有其他工具可以满足他/她的需求,Perl是显而易见的选择。这不是原始问题的答案吗?

–史蒂夫
2014年8月1日在2:54

@a_arias:你是对的;他做到了。但是他无法使用Solaris sed实现他想要的功能。这个问题实际上是一个很好的问题,但是由于无法阅读手册页或不愿意在此处实际阅读问题的人而降低了其价值。

–史蒂夫
14-10-14在1:28



@EdwardGarson:IIRC,Solaris附带Perl,但不附带Python或Ruby。就像我之前说过的那样,OP无法使用Solaris sed实现所需的结果。

–史蒂夫
15年7月23日在1:25

#3 楼

请注意,在OS X上,运行此命令时可能会遇到奇怪的错误,例如“无效的命令代码”或其他奇怪的错误。要解决此问题,请尝试

sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>


,这是因为在OSX版本的sed上,-i选项需要一个extension参数,因此您的命令实际上被解析为extension参数,并且文件路径被解释为命令代码。来源:https://stackoverflow.com/a/19457213

评论


这是OS X的另一个怪异之处。幸运的是,通过gnu-sed的brew安装以及PATH,您可以使用sed的GNU版本。感谢您的提及;明确的头部抓痒器。

–道格
19年7月3日在13:28

#4 楼

以下在我的mac上正常工作

sed -i.bak 's/foo/bar/g' sample


我们用示例文件中的bar替换foo。原始文件的备份将保存在sample.bak中。要进行内联编辑而不进行备份,请使用以下命令

sed -i'' 's/foo/bar/g' sample


评论


有什么办法可以防止保留备份文件?

–Petr Peller
2014年8月1日在8:03

@PetrPeller,您可以将提到的命令用于相同的... sed -i'''s / foo / bar / g'示例

– minhas23
2014年8月1日在9:46

#5 楼

需要注意的一件事是,sed不能单独编写文件,因为sed的唯一目的是充当“流”(即stdin,stdout,stderr和其他>&n缓冲区,套接字等的管道)的编辑器。考虑到这一点,您可以使用另一个命令tee将输出写回到文件中。另一种选择是通过将内容传递到diff来创建补丁。

Tee方法

 sed '/regex/' <file> | tee <file>
 


修补方法

 sed '/regex/' <file> | diff -p <file> /dev/stdin | patch
 


更新:

另外,请注意,patch将使文件从diff输出的第一行进行更改:

Patch不需要知道要访问哪个文件,因为在第一行中可以找到该文件。 diff的输出:

$ echo foobar | tee fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin
*** fubar   2014-03-15 18:06:09.000000000 -0500
--- /dev/stdin  2014-03-15 18:06:41.000000000 -0500
***************
*** 1 ****
! foobar
--- 1 ----
! fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin | patch
patching file fubar


评论


/ dev / stdin 2014-03-15,1月7日回答-您是时间旅行者吗?

– AdrianFrühwirth
2014-4-17 13:55



在Windows上,使用msysgit时/ dev / stdin不存在,因此必须将/ dev / stdin替换为“-”,即不带引号的单个连字符,因此以下命令应该可以使用:$ sed's / oo / u /'fubar | diff -p fubar-和$ sed's / oo / u /'fubar | diff -p fubar-|补丁

– Jim Raden
2014年11月6日在16:30



@AdrianFrühwirth我确实在附近某处有一个蓝色的警察岗亭,由于某种原因,他们确实在工作中称我为“医生”。但说实话,看起来当时系统上的时钟确实非常糟糕。

–德怀特·斯潘塞(Dwight Spencer)
16年4月28日在16:29

在我看来,这些解决方案依赖于特定的缓冲行为。我怀疑对于较大的文件,当sed读取时,这些文件可能会损坏,文件可能会通过patch命令在下面开始更改,甚至更糟糕的是会截断该文件的tee命令。其实我不确定补丁是否也不会截断。我认为将输出保存到另一个文件,然后保存到原始文件会更安全。

–akostadinov
16年5月26日在12:26

来自@ william-pursell的真实就位答案

–akostadinov
16年5月26日在13:09

#6 楼

支持sed选项以就地编辑文件的-i版本写入临时文件,然后重命名该文件。或者,您也可以只使用ed。例如,要将文件foo中所有出现的bar更改为file.txt,您可以执行以下操作:
echo ',s/foo/bar/g; w' | tr \; '2' | ed -s file.txt
sed,但肯定不完全相同。
即使您不这样做没有支持-ised,您可以轻松编写脚本来为您完成工作。代替sed -i 's/foo/bar/g' file,您可以执行inline file sed 's/foo/bar/g'。这样的脚本编写起来很简单。例如:
#!/bin/sh
IN=
shift
trap 'rm -f "$tmp"' 0
tmp=$( mktemp )
<"$IN" "$@" >"$tmp" && cat "$tmp" > "$IN"  # preserve hard links

应足以满足大多数用途。

评论


因此,例如,您将如何命名该脚本以及如何命名?

–两栖动物
2012年10月2日21:00

我将其称为内联并按上述方法调用它:内联输入文件sed's / foo / bar / g'

–威廉·珀塞尔(William Pursell)
2012年10月2日,21:11

哇,ED只回答确实有效的答案。第一次我需要ed。谢谢。一个小的优化是使用echo -e“,s / foo / bar / g \ 012 w”或echo $',s / foo / bar / g \ 012 w',其中shell允许它避免额外的tr调用。

–akostadinov
16年5月26日在13:08

ed并没有真正就地进行修改。 GNU ed从strace输出创建一个临时文件,将所做的更改写入该临时文件,然后重写整个原始文件,并保留硬链接。从truss输出中,Solaris 11 ed也使用了一个临时文件,但是使用w命令保存后,将临时文件重命名为原始文件名,这会破坏所有硬链接。

–安德鲁·亨利(Andrew Henle)
18年1月19日在11:06

@AndrewHenle令人沮丧。当前macOS随附的ed(Mojave,无论行销术语是什么意思)仍保留硬链接。青年汽车

–威廉·珀塞尔(William Pursell)
19/12/2在13:49

#7 楼

您可以使用vi

vi -c '%s/foo/bar/g' my.txt -c 'wq'


#8 楼

sed支持就地编辑。来自man sed

-i[SUFFIX], --in-place[=SUFFIX]

    edit files in place (makes backup if extension supplied)


示例:

假设您有一个文件hello.txt,其文本为:

hello world!


如果要保留旧文件的备份,请使用:

sed -i.bak 's/hello/bonjour' hello.txt


您将最终得到两个文件:hello.txt与内容:

bonjour world!


hello.txt.bak具有旧内容。

如果您不想保留副本,只是不要通过扩展参数。

评论


OP特别提到他的平台不支持此(受欢迎但非标准)选项。

–tripleee
16年1月18日在20:08

#9 楼

如果要替换相同数量的字符,并且在仔细阅读文件的“就地”编辑之后...

还可以使用重定向操作符<>打开文件进行读写: br />
sed 's/foo/bar/g' file 1<> file


实时观看:

$ cat file
hello
i am here                           # see "here"
$ sed 's/here/away/' file 1<> file  # Run the `sed` command
$ cat file
hello
i am away                           # this line is changed now


来自Bash参考手册→3.6.10打开文件描述符进行读取和编写:


重定向运算符

[n]<>word


使名称为word扩展名的文件打开为
在文件描述符n上读写,如果未指定n,则在文件描述符0
上读写。如果文件不存在,则会创建该文件。


评论


这已被文件损坏。我不确定其背后的原因。 paste.fedoraproject.org/462017

– Nehal J Wani
16-10-27在19:09

见[43-44],[88-89],[135-136]行

– Nehal J Wani
16-10-27在19:15

该博客文章解释了为什么不应该使用此答案。 backreference.org/2011/01/29/就地编辑文件

– Nehal J Wani
16-10-27在19:22

@NehalJWani我会仔细阅读这篇文章,感谢大家的注意!我没有在答案中提及的是,如果更改相同数量的字符,则此方法有效。

– fedorqui'停止伤害'
16-10-28在6:51

有人说@NehalJWani,很抱歉,如果我的回答对您的文件造成了损害。阅读您提供的链接后,我将对其进行更正(或在必要时删除),以进行更正。

– fedorqui'停止伤害'
16-10-28在13:04

#10 楼

您没有指定要使用的Shell,但是使用zsh可以使用=( )构造来实现。类似于以下内容:

cp =(sed ... file; sync) file


=( )>( )类似,但是创建了一个临时文件,当cp终止时,该文件会自动删除。

#11 楼

mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt


应保留所有硬链接,因为输出被定向回以覆盖原始文件的内容,并且避免了需要特殊版本的sed。

#12 楼

就像Moneypenny在Skyfall中说的那样:“有时候旧方法是最好的。”
Kincade稍后也说了类似的话。

$ printf ',s/false/true/g\nw\n' | ed {YourFileHere}


就地编辑很愉快。
/>添加了'\ nw \ n'来写入文件。抱歉延迟回复请求。

评论


您能解释一下这是什么吗?

–马特·蒙塔格(Matt Montag)
18年1月16日在7:53

它调用文本编辑器ed(1),并将某些字符写入stdin。作为30岁以下的人,我认为对恐龙来说ed(1)对我们来说就像恐龙一样。无论如何,jlettvin不用打开ed并在其中输入内容,而是使用|来传递字符。 @马特蒙塔格

–阿里·斯威德勒(Ari Sweedler)
18年8月14日在16:13

如果您询问命令的功能,则有两个命令以\ n终止。 [range] s / / / 是替代命令的形式-在许多其他文本编辑器中仍然可以看到的范例(好的,vim,ed的直接后代)无论如何,g标志站立代表“全局”,表示“您查看的每一行上的每个实例”。范围3,5查看3-5行。 ,5查看StartOfFile-5,3查看行3-EndOfFile和,查看StartOfFile-EndOfFile。每行上的全局替换命令,将“ false”替换为“ true”。然后,输入写命令w。

–阿里·斯威德勒(Ari Sweedler)
18年8月14日在16:17



#13 楼

很好的例子。我面临着就地编辑许多文件的挑战,并且-i选项似乎是在find命令中使用它的唯一合理的解决方案。在此脚本中,在每个文件的第一行之前添加“版本:”:

find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;