在sed的替换部分中使用时,为什么这些简单的shell命令会失败

时间:2013-05-21 15:02:36

标签: macos bash shell sed awk

在尝试找到 this sed question 的答案时,我想出了一个我无法理解的奇怪行为。

假设我有一个名为data

的文件
$> cat data
foo.png
abCd.png
bar.png
baZ.png

任务是使用sed在行中将所有带有大写ASCII字符的行替换为小写。所以输出应该是:

$> cat data
foo.png
abcd.png
bar.png
baz.png

该解决方案应该适用于非gnu sed,也就像在Mac上的sed一样

我尝试将这个嵌入式awk放入sed的替换部分:

sed -E 's/[^ ]*[A-Z][^ ]*.png/'$(echo \&|awk '{printf("<%s>[%s]",$0, tolower($0))}')'/' data

奇怪的是,这输出了:

foo.png
<abCd.png>[abCd.png]
bar.png
<baZ.png>[baZ.png]

正如你所看到的那样,sed正在拾取带有大写字母的正确行,并且那也达到awk但awk的tolower()函数失败并产生与输入相同的文本。

shell专家可以解释这种奇怪的行为。

7 个答案:

答案 0 :(得分:10)

您的awk命令在sed命令之前运行,而不是作为sed命令的子进程运行,因此awk仅接收文字&符作为其输入,如结果输出

<&>[&]

然后将此字符串嵌入到sed作为其参数接收的字符串中,从中可以明显看出sed产生输出的原因。

事件顺序是

  1. shell看到此命令行

    sed -E 's/[^ ]*[A-Z][^ ]*.png/'$(echo \&|awk '{printf("<%s>[%s]",$0, tolower($0))}')'/' data
    
  2. 它处理命令替换(其中awk&转换为<&>[&]),以生成中间命令行

    sed -E 's/[^ ]*[A-Z][^ ]*.png/'<&>[&]'/' data
    
  3. 然后shell使用命令sed执行s/[^ ]*[A-Z][^ ]*.png/<&>[&]/

答案 1 :(得分:6)

sed 'y/ABCDEFGHIJKLMNOPQRSYUVWXYZ/abcdefghijklmnopqrstuvwxyz/'

答案 2 :(得分:3)

也许tr正是你真正想要的?

tr A-Z a-z file

sed等价物将是:

sed -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'

您似乎不能使用字符范围表示法(A-Z和/或[A-Z]),这是不幸的,也很烦人。

答案 3 :(得分:2)

我99%确定你不能直接在Mac / BSD sed上执行此操作而没有特别难看的东西(sed -e s/A/a/g -e s/B/b/g ...),所以除非找到sed解决方案,这是一个awk,它内联:

awk '{print tolower($0) >FILENAME}' data

答案 4 :(得分:2)

如果你真的需要在普通sed中转换为小写,那么它可能会相当丑陋:

sed -e s/A/a/g -e s/B/b/g -e s/C/c/g -e s/D/d/g -e s/E/e/g -e s/F/f/g -e s/G/g/g -e s/H/h/g -e s/I/i/g -e s/J/j/g -e s/K/k/g -e s/L/l/g -e s/M/m/g -e s/N/n/g -e s/O/o/g -e s/P/p/g -e s/Q/q/g -e s/R/r/g -e s/S/s/g -e s/T/t/g -e s/U/u/g -e s/V/v/g -e s/W/w/g -e s/X/x/g -e s/Y/y/g -e s/Z/z/g
编辑:没关系,@ Bruce Barnett的解决方案更好

答案 5 :(得分:2)

你真的确定你不能使用Perl吗?

perl -pi.bak -e 's/([^ ]*[A-Z][^ ]*\.png)/\l\1/' file

这是反斜杠 - 指定小写,反斜杠 - 重复第一个匹配组。

答案 6 :(得分:1)

根据一些良好的答案,这里有一个解决方案我可以提出:

sed -i.bak 'y/'$(awk 'BEGIN {for(i=65; i<=90; i++) printf("%c", i); printf("/");
                             for(i=97; i<=122; i++) printf("%c", i)}')'/' data