Поменять местами 2 слова без использования третьего слова

У меня есть файл с именем swap:

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

Я хочу изменить все вхождения BILL на CLINTON и одновременно изменить все вхождения CLINTON на 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

не определено.

-2
26.06.2017, 00:50
3 ответа

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 рассматривает пробел (s )как разделитель полей по умолчанию)

  • if($i=="BILL") $i="CLINTON"-если значение поля равноBILL-назначьте его с помощьюCLINTON

  • else if($i=="CLINTON") $i="BILL"-иначе, если значение поля равно CLINTON-, назначьте его с помощьюBILL

2
28.01.2020, 05:14

Другое решение 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
1
28.01.2020, 05:14
perl -pe 's/(BILL)|CLINTON/$1 ? "CLINTON" : "BILL"/eg' swap

Пояснение:

Опция

-pзаставляет Perlчитать файл а-ля awk, строка -по строке -И текущая запись автоматически -печатается после применения всех преобразований.

Команда s///работает с текущей строкой. Модификаторы шаблона /egare:/gприменит преобразование глобально к текущей строке, а не просто ограничьтесь первым, если вы не упомянули /g. /eмодификаторeval-uate, который делает RHSиз s///принятым как код Perl, и после его оценки значение, которое он дает, принимается как как РГО.

Итак, s/ (BILL )|CLINTON/.../ будет искать BILL или CLINTON в текущей записи. начиная сканирование слева. Когда найден BILL, устанавливается значение $1 и, следовательно, выражение Perl $1 ? "CLINTON" :"BILL" будет иметь значение CLINTON, на которое BILL заменяется в текущей записи. Но мы еще не закончили, за счет модификатора /g. Точно так же, если CLINTON найден, то $1 пуст и поэтому выражение Perl $1 ? "CLINTON" :"BILL" оценивается как BILL и который это то, что КЛИНТОН заменен в отчете. Это продолжается до конца ток достигнут, и в этот момент он выводится в STDOUT из-за опции -p.

sed -e 's/BILL\|CLINTON/\n&/g;s/\nBILL/CLINTON/g;s/\nCLINTON/BILL/g' swap
3
28.01.2020, 05:14

Теги

Похожие вопросы