7

在试图找到这个 sed 问题的答案时,我想出了一个我无法理解的奇怪行为。

假设我有一个名为data

$> cat data
foo.png
abCd.png
bar.png
baZ.png

任务是使用 sed in line 将所有大写 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 但tolower()awk 的功能失败并产生与输入相同的文本。

shell专家可以解释一下这种奇怪的行为吗?

4

7 回答 7

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. 然后shellsed使用命令执行s/[^ ]*[A-Z][^ ]*.png/<&>[&]/

于 2013-05-21T15:10:55.333 回答
7
sed 'y/ABCDEFGHIJKLMNOPQRSYUVWXYZ/abcdefghijklmnopqrstuvwxyz/'
于 2013-05-21T15:57:00.620 回答
3

也许tr是你真正想要的?

tr A-Z a-z file

sed相当于:

sed -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'

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

于 2013-05-21T15:38:29.957 回答
2

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

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

反斜杠-ell 指定小写字母,反斜杠-one 重复第一个匹配组。

于 2013-05-21T17:55:36.643 回答
2

如果您确实需要在 plain 中转换为小写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 的解决方案更好

于 2013-05-21T15:51:20.680 回答
2

我 99% 确定你不能直接在 Mac/BSD 上执行此操作而没有特别难看的东西sedsed -e s/A/a/g -e s/B/b/g ...sedawk

awk '{print tolower($0) >FILENAME}' data
于 2013-05-21T15:21:52.530 回答
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
于 2013-05-21T17:47:20.703 回答