Как перевернуть последний символ строки

#awk

#awk

Вопрос:

Я пытаюсь перевернуть последний символ строки между 1 и 2. Мне нужно понять, почему system2 может конвертировать в system1 , однако system1 не может конвертировать в system2 ?

 echo system2|awk -vFS="" '{if($NF==1) {gsub($NF,2) };if($NF==2)gsub($NF,1)}1'
system1
echo system1|awk -vFS="" '{if($NF==1) {gsub($NF,2) };if($NF==2)gsub($NF,1)}1'
system1
  

Кроме того, есть ли какой-нибудь более разумный способ сделать это переворачивание?

Комментарии:

1. Зачем вам это нужно awk для этого? Для этого очень хорошо можно использовать встроенные модули оболочки

2. У вас есть много ответов awk, которые основаны на установке FS в значение null, разделяющем входные данные на символы. Это неопределенное поведение для POSIX, и поэтому он будет делать это в некоторых awks, в то время как в других awks он оставит ввод в виде одной строки в 1 поле, а в других awks он все еще может делать что угодно еще и по-прежнему соответствовать POSIX.

Ответ №1:

Чтобы сделать это кратко, надежно и переносимо с любым awk, было бы:

 $ echo system2| awk '!sub(/1$/,2){sub(/2$/,1)}1'
system1
$ echo system1| awk '!sub(/1$/,2){sub(/2$/,1)}1'
system2
  

Ответ №2:

Вы могли бы попробовать следующее (исправление попытки OP).

 echo system1|awk -vFS="" '{if($NF==1){gsub($NF,2);print;next};if($NF==2)gsub($NF,1)}1'
  

Почему попытка OP не работает:

Поскольку первое условие ИСТИННО, а $ NF равен 1 , оно изменяет его на 2 , теперь второе условие ИСТИННО и 2 снова заменяется на 1 , следовательно, оно выдает тот же результат.

Моей попыткой решения этой проблемы будет: Поскольку код, который пытался использовать OP, очень специфичен для GNU awk , поэтому мы могли бы попробовать следовать тому, что могло бы быть более общим решением.

 echo system1 | 
awk '
{
   val=substr($0,length($0))
   printf("%s%sn",substr($0,1,length($0)-1),val==1 0?2:(val==2?1:val))
   val=""
}'
  

Комментарии:

1. Первое решение не работает для обоих входных данных (у него та же проблема, но для другого ввода).

2. @choroba, спасибо, сэр, что сообщили, изменил это сейчас.

Ответ №3:

Это потому, что вы меняете $ NF на 2, что делает второе условие истинным, и оно снова изменяется на 1. Вам нужен else .

 awk -vFS="" '{if($NF==1) {gsub($NF,2) }else if($NF==2)gsub($NF,1)}1'
  

Я могу придумать немного более простое решение в sed:

 sed 's/1$/2/;t;s/2$/1/'
  

в котором используется t команда, которая (если метка не была указана) переходит в конец скрипта, если предыдущая замена прошла успешно.

Другое возможное решение использует Perl:

 perl -pe 's/([12])$/ $1 =~ tr=12=21=r /e'
  

Который соответствует последнему 1 или 2 и выполняет для него транслитерацию. Вы можете использовать аналогичный трюк с tr в командной строке:

 printf '%s' ${string:0:-1}
printf '%sn' ${string: -1} | tr 12 21
  

Не разбирая, вы можете использовать, например, расширение параметров и арифметику:

 if [[ $string = *[12] ]] ; then
   echo ${string%[12]}$((3-${string: -1}))
else
    echo $string
fi
  

Комментарии:

1. Этот awk-скрипт плохой. Для начала он полагается на установку FS в значение null, разделяющее строку на символы, но это не определено для каждого POSIX, поэтому на самом деле он будет делать разные вещи в разных awks. Затем, если он ДЕЙСТВИТЕЛЬНО разделит входные данные на символы, он заменит каждое вхождение последнего символа во всей строке либо на 1, либо на 2.

Ответ №4:

 awk 'BEGIN{FS=OFS=""}($NF==1||$NF==2){$NF=$NF%2 1}1'
  

Это похоже на подход oguzismail.

  • Изменяется, только если последний символ 1 или 2 .
  • 1 % 2 1 = 2 , и 2 % 2 1 = 1 .