为什么sed无法将\ t识别为标签?


105
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename

我期望此sed脚本tab在每行之前插入一个,$filename但不是。由于某种原因,它t改为插入一个。


1
由于sed在不同平台之间(尤其是BSD / MacOSX与Linux)可能会有所不同,因此指定使用sed的平台可能会有所帮助。
艾萨克(Isaac)2010年

sed“ s /(。*)/#\ 1 /” $ filename | tr'#''\ t'> $ sedTmpFile && mv $ sedTmpFile $文件名。
user2432405

对于OS X(macOS)用户,请参考此问题
富兰克林于

Answers:


129

并非所有版本的sed谅解\t。只需插入文字标签即可(按Ctrl- V然后按Tab)。


2
是的; 需要澄清的是:并非sed的所有版本都可以\t在表达式的替换部分中理解(\t在模式匹配部分中可以识别)
John Weldon 2010年

3
awwwwwwwwwwwwwwwwwwwwww,好的,这很有趣。真奇怪 为什么要让它在一处而不是另一处识别呢?
sixtyfootersdude 2010年

2
从脚本调用,将不起作用:sh将忽略选项卡。例如,以下来自shell脚本的代码将添加$ TEXT_TO_ADD,而不以列表开头:sed“ $ {LINE} a \\ $ TEXT_TO_ADD” $ FILE。
Dereckson

2
@Dereckson和其他人-查看此答案:stackoverflow.com/a/2623007/48082
Cheeso 2013年

2
德雷克森可以/不能/?
道格拉斯于2015年

41

使用Bash,您可以通过编程方式插入TAB字符,如下所示:

TAB=$'\t' 
echo 'line' | sed "s/.*/${TAB}&/g" 
echo 'line' | sed 's/.*/'"${TAB}"'&/g'   # use of Bash string concatenation

这非常有帮助。
Cheeso

1
您的前进方向正确,$'string'但缺乏解释。实际上,我怀疑,由于用法极其笨拙,您可能没有完全理解(就像我们大多数人对bash所做的那样)。请参阅下面的说明:stackoverflow.com/a/43190120/117471
Bruno Bronosky

1
请记住,BASH不会像$TAB单引号内那样扩展变量,因此您需要使用双引号。
nealmcb

小心使用*内部双引号...这将被视为全局,而不是您想要的正则表达式。
levigroker

27

@sedit在正确的路径上,但是定义变量有点尴尬。

解决方案(特定于重击)

用bash进行此操作的方法是在单引号引起的字符串前加一个美元符号。

$ echo -e '1\n2\n3'
1
2
3

$ echo -e '1\n2\n3' | sed 's/.*/\t&/g'
t1
t2
t3

$ echo -e '1\n2\n3' | sed $'s/.*/\t&/g'
    1
    2
    3

如果您的字符串需要包含变量扩展,则可以将带引号的字符串放在一起,如下所示:

$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958  1
1491237958  2
1491237958  3

说明

在bash中$'string'导致“ ANSI-C扩展”。这也正是我们大多数人的预料,当我们使用之类的东西\t\r\n等来源:https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting

$'string'形式的单词经过特殊处理。该单词扩展为string,并按ANSI C标准的规定替换了反斜杠转义的字符。反斜杠转义序列(如果存在)将被解码...

扩展结果是单引号,好像没有美元符号。

解决方法(如果必须避免使用bash)

我个人认为,避免bash的大多数工作都是愚蠢的,因为避免bashisms不会*使您的代码可移植。(bash -eu如果您猛击它,您的代码将比尝试避免使用bash并使用sh[除非您是绝对的POSIX忍者]少一些。)但是,与其对此有一个虔诚的争论,我只会给您最好的*回答。

$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
    1
    2
    3

* 最佳答案?是的,因为大多数抗的bash shell脚本的人做错事在他们的代码一个例子是使用echo '\t'@ robrecord的答案。这将适用于GNU回声,但不适用于BSD回声。开放小组(The Open Group)在http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16上对此进行了解释,这是为什么尝试避免宗教信仰失败的一个例子。


8

我已经在Ubuntu 12.04(LTS)上的Bash shell中使用了类似的东西:

要追加一个新行标签,第二第一行匹配:

sed -i '/first/a \\t second' filename

首先tab,second替换:

sed -i 's/first/\\t second/g' filename

4
两次转义是关键,即使用\\t而不是\t
zamnuts's

在Ubuntu 16.04和Bash 4.3上,我还必须使用双引号而不是单引号。
CAW

4

$(echo '\t')。您将需要在模式周围加引号。

例如。删除标签:

sed "s/$(echo '\t')//"

5
有趣的是,您正在使用“ GNU echo”特定功能(将\ t解释为制表符)来解决“ BSD sed”特定的错误(将\ t解释为2个单独的字符)。大概,如果您具有“ GNU echo”,那么您也将具有“ GNU sed”。在这种情况下,您将不需要使用echo。使用BSD,echo echo '\t'将输出2个单独的字符。POSIX可移植的方式是使用printf '\t'。这就是为什么我说:不要尝试通过不使用bash来使代码可移植。这比您想的要难。使用bash是我们大多数人最可移植的事情。
布鲁诺·布罗诺斯基

3

sed实际上,您只需要在行的前面插入一个制表符,就无需使用替换。与仅打印出来相比,这种情况下的替换操作是昂贵的操作,尤其是在处理大文件时。它也更容易阅读,因为它不是正则表达式。

例如使用awk

awk '{print "\t"$0}' $filename > temp && mv temp $filename


0

sed不支持\t,也没有其他类似的转义序列\n。我发现这样做的唯一方法是使用以下命令在脚本中实际插入制表符sed

也就是说,您可能要考虑使用Perl或Python。这是我写的一个简短的Python脚本,用于所有流正则表达式:

#!/usr/bin/env python
import sys
import re

def main(args):
  if len(args) < 2:
    print >> sys.stderr, 'Usage: <search-pattern> <replace-expr>'
    raise SystemExit

  p = re.compile(args[0], re.MULTILINE | re.DOTALL)
  s = sys.stdin.read()
  print p.sub(args[1], s),

if __name__ == '__main__':
  main(sys.argv[1:])

2
Perl版本将是一线外壳的“ perl -pe's / a / b /'文件名”或“某物| perl -pe's / a / b /'”
tiftik 2010年

0

代替BSD sed,我使用perl:

ct@MBA45:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g"
   hi

0

我认为其他人的其他方法(充分澄清这个sedAWK等)。但是,bash接下来是我特定的答案(在macOS High Sierra和CentOS 6/7上测试)。

1)如果OP想要使用与他们最初提出的搜索和替换方法类似的搜索和替换方法,那么我建议perl如下使用。注意:不需要在正则表达式的括号前加反斜杠,并且此代码行反映了与替换运算符$1相比\1,更好的用法perl(例如,根据Perl 5文档)。

perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename

2)但是,正如ghostdog74所指出的那样,由于所需的操作实际上是在将tmp文件更改为输入/目标文件()之前简单地在每行的开头添加一个选项卡,所以$filenameperl再次建议但进行以下修改(s):

perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename

3)当然,tmp文件是多余的,因此最好“按原样”完成所有操作(添加-i标志),并使用简化的内容简化为更简单的单行代码

perl -i -pe $'s/^/\t/' $filename
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.