解释:

解释:

我有一个名为swap

$ cat swap
aardvark BILL CLINTON dog
eternal CLINTON BILL forever
BILL good housekeeping BILL

我想更改所有出现的BILLtoCLINTON 并同时更改所有出现的CLINTONto BILL,结果输​​出为:

aardvark CLINTON BILL dog
eternal BILL CLINTON forever
CLINTON good housekeeping CLINTON

我知道经典的解决方案:

sed -e 's/BILL/temp/g' -e 's/CLINTON/BILL/g' -e 's/temp/CLINTON/g'

但有人向我提出挑战,作为练习,要求我不要使用第三个词。

我正在使用以下命令,但它仅在第一行起作用:

$ sed 's/\(BILL\) \(CLINTON\)/\2 \1/g' swap
aardvark CLINTON BILL dog
eternal CLINTON BILL forever
BILL good housekeeping BILL

注意:我使用的是 Solaris 10 操作系统。

在观察这种人为约束的同时,如何获得所需的输出?

行为

MORBILLIFORM OVERBILLED
TOM, BILL, AND HARRISON

未指定。

答案1

perl -pe 's/(BILL)|CLINTON/$1 ? "CLINTON" : "BILL"/eg' swap

解释:

-p选项使得Perl读取文件 la awk,逐行并且当前记录在应用所有转换后自动打印。

s///命令在当前行起作用。模式/eg修饰符是:/g将在当前行全局应用转换,而不仅仅是将其自身限制为第一行(如果您没有提到/g.是-uate/e修饰符eval,它使 的RHSs///视为Perl代码,并且在评估后其结果的值被视为 RHS。

因此,s/(BILL)|CLINTON/.../ 将从左侧开始扫描,在当前记录中查找 BILL 或 CLINTON。当找到 BILL 时,$1 就被设置,因此 Perl 表达式 $1 ? "CLINTON" : "BILL" 将计算为 CLINTON,这是当前记录中 BILL 被替换的内容。但由于修饰符,我们已经完成了/g。同样,如果找到 CLINTON,则 $1 为空,因此 Perl 表达式 $1 ? "CLINTON" : "BILL" 被计算为 BILL,这就是 CLINTON 在这个记录一直持续到当前达到的末尾,此时由于该-p选项,它被打印到 STDOUT。

sed -e 's/BILL\|CLINTON/\n&/g;s/\nBILL/CLINTON/g;s/\nCLINTON/BILL/g' swap

答案2

awk解决方案:

awk '{ for(i=1;i<=NF;i++) { if($i=="BILL") $i="CLINTON"; else if($i=="CLINTON") $i="BILL" } }1'  swap

输出:

CLINTON BILL
BILL CLINTON
CLINTON CLINTON

  • for(i=1;i<=NF;i++)- 迭代所有字段(awk将空格视为默认字段分隔符)

  • if($i=="BILL") $i="CLINTON"- 如果字段值等于BILL- 为其分配CLINTON

  • else if($i=="CLINTON") $i="BILL"- 否则,如果字段值等于CLINTON- 将其分配给BILL

答案3

另一个 awk 解决方案

awk '{
        while (match($0, /BILL|CLINTON/)) {
            printf "%s", substr($0, 1, RSTART-1);
            $0 = substr($0, RSTART);
            printf "%s", /^BILL/ ? "CLINTON" : "BILL";
            $0 = substr($0, 1+RLENGTH)
        }
        print
    }' swap

相关内容