我可以使用`sed`来翻译`tr`这样的字符吗?


14

我想用另一组中的相应字符替换一组字符,如下所示:

original set: ots
"target" set: u.x

foobartest → fuubar.ex.

这样的翻译/音译是tr命令的特长:

$ echo 'foobartest' | tr 'ots' 'u.x'
fuubar.ex.

不幸的是,tr它不支持就地更改文件sed
我想使用它,sed所以我不必重新发明处理临时文件的方式。


自问这个问题,因为我似乎找不到“ sed翻译字符”的任何结果。magic关键字最终是“音译”,但我认为值得使此功能尽可能容易找到。
n.st

尝试实现此变通办法时要记住的几点:(tr正确)忽略替换集中的递归:echo 'abc' | tr ab bxbxc。一个原始的解决方案可能会避免这种情况,xxc因为它会将翻译重新应用于已经翻译过的字符。
n.st

相关:tr模拟的unicode字符?(GNU sed违背GNU tr可以音译多字节字符)
斯特凡Chazelas

如果您想要另一种可能性:perl可以翻译,并且-i和(除非是古老的)多字节。不是POSIX,但很常见。
dave_thompson_085

Answers:


24

sed具有如下y命令tr

$ echo 'foobartest' | sed 'y/ots/u.x/'
fuubar.ex.

y命令是POSIX sed规范的一部分,因此它几乎可以在任何平台上运行。

既然是sed,您就可以用编辑后的版本替换它,从而省去了繁琐的临时文件业务(前提是您对POSIX未指定sed支持该-i选项的实现):

$ sed -i 'y/ots/u.x/' some-file.txt

@StéphaneChazelas感谢您指出这一点;直到现在我才知道内部的运作方式。我已经编辑了答案以提及这一点。
n.st

谢谢,这非常有用!我原以为它可以在VIM(在CentOS 7.3上为8.0.1092)中工作,但事实并非如此。sed不应该做任何事情,VIM吗?
dotancohen

1
@dotancohen仅仅因为Vim的替代函数是在seds 之后建模的,并不意味着其他函数也是如此。;)Vim邮件列表中有一个寻找对y/abc/def/等邮件的主题;最好的选择似乎是:%call setline(".", tr(getline("."),"abc","def"))
n.st

8

如果像您的情况一样,您在不改变字符大小的情况下音译字符(无论如何,某些实现,例如GNU tr仅支持单字节字符),则可以执行以下操作:

tr 'ots' 'u.x' < file 1<> file

也就是说,已经tr覆盖了文件本身。

这比sed -i在几个帐户上要好:

  • 它不需要额外的磁盘空间(某些稀疏文件,写时复制特殊情况除外)
  • 它保留了索引节点编号,所有权,权限,ACL ...
  • 它可以通过符号链接正常工作,不会破坏硬链接
  • 它不会在被杀死时留下临时文件。

一个缺点是,如果文件被打断,它将最终被半翻译(不过,在这种情况下,您可以再次运行它来完成文件)。sed除非命令成功执行,否则某些实现将通过确保原始文件保持不变来正确处理该问题。


3
如果翻译集中存在递归,请小心重新运行翻译echo 'abc' | tr ab bx
n.st

1
@ n.st,是的,这就是我在这种情况下说的原因,尽管我同意值得说明。
斯特凡Chazelas

最后,我毕竟必须使用临时文件:gist.github.com/n-st/048facd0c12f105ac122030fb58b962f —由于多字节字符无法使用GNU tr,在我们的符号链接繁重的PXE环境中,这sed -i是一个漫长的等待发生……:/
n.st

@ n.st,iconv -t cp437似乎更合适。
斯特凡Chazelas

iconv当输入文件已包含cp437编码的字节或多种编码的组合时中断。因此,尽管在一般情况下更可取,但在这种情况下进行手动替换更为健壮。
n.st

4

作为另一种选择,如果您的主要问题是缺乏对就地更改文件的支持,那么您可能会对moreutils软件包中的sponge工具感兴趣:

tr 'ots' 'u.x' < file | sponge file

将写入file,但仅file在输入完成后才可以写入。从联机帮助页

sponge读取标准输入并将其写到指定文件中。与shell重定向不同,海绵在打开输出文件之前先吸收所有输入。这允许构造读取和写入同一文件的管道。

除非您有无法保存在内存中的非常大的文件,否则它们可以sponge为您工作。


2
有一个问题sponge是,它仍然覆盖file,如果tr失败(例如,如果你有写而不是读访问权file
斯特凡Chazelas

哦,确实如此;没想到 谢谢。
mindriot

请参阅cat file >; fileksh93 的运算符,该运算符将输出写入临时文件,仅在命令成功后才将其重命名为目标sed -i文件(但与一样,该文件将创建一个新文件而不是覆盖原始文件)。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.