Una soluciónbash
(4.0+ )que construye una tabla lookup
(una matriz asociativa )utilizando las teclas en mayúsculas -inferiores de VAR1
y los valores asociados. Luego revisa los valores en VAR2
y construye VAR3
con valores de la tabla de búsqueda, o de VAR2
si no hay ninguna clave en la tabla de búsqueda correspondiente a la cadena VAR2
actual.
VAR1=tom:rich,LIAm:viki
VAR2=liam,albert,tom
declare -A lookup
# build lookup table
while read -d, key_value; do
# $key_value is a string like "tom:rich", separate these into key and value:
IFS=: read key value <<<"$key_value"
# add lower-cased key to table with value
lookup[${key,,}]=$value
done <<<"$VAR1,"
# do lookups in table
while read -d, string; do
# get newstring from table, but use $string if there's no entry:
newstring=${lookup[${string,,}]:-$string}
# add $newstring to VAR3, with a delimiting comma unless VAR3 is empty
VAR3+="${VAR3:+,}'$newstring'"
done <<<"$VAR2,"
printf 'VAR3 = %s\n' "$VAR3"
Este código genera
VAR3 = 'viki','albert','rich'
Esto supone que los valores en VAR1
y VAR2
no contienen líneas nuevas.
Чистый раствор sed
без трубопроводов иtac
В таком случае линейный -за -линейный подход из sed
не помогает. Лучше обработать весь буфер сразу, так как вариант -z
GNU sed
делает (вы, кажется, используете linux и GNU sed
, переносную замену см. в этом Q&A).
Теперь вы можете воспользоваться жадным характером.*
:Шаблон .*apple1
будет соответствовать всему, включая последнее вхождение apple1
, потому что все остальные вхождения поглощаются .*
.
Затем просто добавьте следующие поля(\s+
для разделителя столбцов, [0-9]+
для второго столбца и еще один \s+
, все расширенные регулярные выражения GNU )и окружите их ()
, чтобы вы могли использовать их повторно. в замене как \1
. Затем добавьте третий столбец за пределами ()
, чтобы заменить его, и получится
sed -zE 's/(.*\napple1\s+[0-9]+\s+)[0-9]+/\14444444/'
Вот именно.
Примечание для пользователей, не -GNU sed
:Портативное решение было бы менее удобным:
sed -E 'H;1h;$!d;x;s/(.*\napple1[[:space:]]+[0-9]+[[:space:]]+)[0-9]+/\14444444/'
tac file |
awk -v string='apple1' -v replace='444444444' '
!flag && $1 == string { $3 = replace; flag = 1 }
{ print }' |
tac
Этот конвейер сначала меняет порядок строк в данных, используя tac
из GNU coreutils. Последняя строка, где 1-й столбец представляет собой определенную строку, легче найти таким образом.
Команда awk
просто сравнивает первый столбец с заданной строкой, и если мы еще не сделали замену(!flag
не -ноль ), мы изменяем третий столбец, как только мы найти строку в 1-м столбце. При этом мы также устанавливаем flag
в единицу, чтобы не производить дальнейших замен.
Остальная часть программы awk
просто печатает текущую строку (, включая измененную ).
В конце конвейера мы снова меняем порядок строк с помощью tac
.
Результатом этого, учитывая данные в вопросе, является
apple1 10109283 20012983
apple1 10983102 10293809
apple1 10293893 444444444
apple10 109283019 109238901
apple10 192879234 234082034
apple10 234908443 3450983490
Столбцы в измененной строке немного отличаются от столбцов в других строках из-за модификации 3-го столбца. Чтобы он выглядел лучше, вы можете пропустить результат через дополнительную стадию column -t
в конце конвейера. Если вы это сделаете, вывод будет выглядеть как
apple1 10109283 20012983
apple1 10983102 10293809
apple1 10293893 444444444
apple10 109283019 109238901
apple10 192879234 234082034
apple10 234908443 3450983490
с несколькими пробелами между столбцами.
С sed
это не так просто, как просто заменить 3-й столбец в первой строке, где строка встречается в 1-м столбце (, предполагая, что мы меняем местами строки данных, как в приведенном выше конвейере ). Мы также должны не заменять 3-й столбец в любых последующих строках, даже если 1-й столбец соответствует нашей строке.
Это sed
сценарий редактирования, который делает это правильно (может быть любое количество его вариантов, которые могут работать):
/^apple1\>/ ! {
p
d
}
s/[[:digit:]]*$/444444444/
:loop
n
$ ! b loop
Первая часть заботится о печати строк в начале ввода, которые не соответствуют apple1
в первом столбце.\>
в выражении соответствует концу слова apple1
, так что мы случайно не найдем apple10
или apple12
или любую другую подобную строку.p
(print )иd
(delete + continue со следующей строкой сверху скрипта )внутри {... }
выполняются для каждой строки в начале ввода, которая не . ] соответствует выражению.
Команда s
(замена )выполняется для первой строки ввода, в которой соответствует apple1
в начале строки. Он просто заменяет строку цифр в конце строки на наши 4
s.
Затем следует секция с меткой loop
, которая заботится о передаче остальных данных без изменений путем печати текущей строки и чтения следующей строки с помощьюn
(n
выполняет как печать, так и чтение ). «Текущая строка» будет изменена командой s
при первом проходе по этому циклу.
Самая последняя строка возвращается к метке loop
, если мы еще не достигли последней строки ввода.
Пример запуска:
$ tac file | sed -f script.sed | tac
apple1 10109283 20012983
apple1 10983102 10293809
apple1 10293893 444444444
apple10 109283019 109238901
apple10 192879234 234082034
apple10 234908443 3450983490
Пробовал использовать команду «Нижний», все работает нормально
for i in `awk '{print $1}' file1| awk '{if(!seen[$1]++)print }'`; do j=`awk -v i="$i" '$1 == i {print $0}' file1| awk '{print NR}'| sed -n '$p'`; awk -v i="$i" '$1 == i {print $0}' file1|awk -v i="$i" -v j="$j" 'NR==j{$3="444444444"}1'; done