У меня есть файл с именем 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
не определено.
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
Другое решение 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
perl -pe 's/(BILL)|CLINTON/$1 ? "CLINTON" : "BILL"/eg' swap
-p
заставляет Perl
читать файл а-ля awk
, строка -по строке -И текущая запись автоматически -печатается после применения всех преобразований.
Команда s///
работает с текущей строкой. Модификаторы шаблона /eg
are:/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