#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
.