Что такое эффективный способ заменить список строк другим списком в файле Unix?
Предположим, что у меня есть два списка строк (список A и список B) с таким же количеством записей, N, в каждом списке, и я хочу заменить все вхождения n-го элемента A на n-й элемент B в файле в Unix (в идеале с использованием Bash).
Каков наиболее эффективный способ сделать это?
Неэффективным способом было бы сделать N вызовов "sed s/stringA/stringB/g
".
Ответы
Ответ 1
Это сделает это за один проход. Он считывает listA и listB в awk-массивы, затем для каждой строки linput проверяет каждое слово, и если слово найдено в списке A, слово заменяется соответствующим словом в listB.
awk '
FILENAME == ARGV[1] { listA[$1] = FNR; next }
FILENAME == ARGV[2] { listB[FNR] = $1; next }
{
for (i = 1; i <= NF; i++) {
if ($i in listA) {
$i = listB[listA[$i]]
}
}
print
}
' listA listB filename > filename.new
mv filename.new filename
Я предполагаю, что строки в listA не содержат пробелов (разделитель полей по умолчанию awk)
Ответ 2
Сделать один вызов sed
, который пишет sed script, а другой - использовать его? Если ваши списки находятся в файлах listA
и listB
, то:
paste -d : listA listB | sed 's/\([^:]*\):\([^:]*\)/s%\1%\2%/' > sed.script
sed -f sed.script files.to.be.mapped.*
Я делаю некоторые радикальные предположения о "словах", не содержащих ни двоеточия, ни символа процента, но вы можете адаптироваться к этому. Некоторые версии sed
имеют верхние границы количества команд, которые могут быть указаны; если это проблема, потому что списки слов достаточно велики, вам может понадобиться разделить созданный sed script на отдельные файлы, которые применяются, или изменить использование чего-либо без ограничения (например, Perl).
Другой элемент, о котором нужно знать, - это последовательность изменений. Если вы хотите поменять два слова, вам нужно тщательно составить свои списки слов. В общем случае, если вы сопоставляете (1) wordA с wordB и (2) wordB с wordC, то имеет значение, делает ли sed script отображение (1) до или после отображения (2).
Показанный script не относится к границам слов; вы можете сделать это осторожно по-разному, в зависимости от версии sed
, которую вы используете, и ваших критериев для того, что составляет слово.
Ответ 3
Мне нужно было сделать что-то подобное, и я закончил генерировать команды sed на основе файла карты:
$ cat file.map
abc => 123
def => 456
ghi => 789
$ cat stuff.txt
abc jdy kdt
kdb def gbk
qng pbf ghi
non non non
try one abc
$ sed `cat file.map | awk '{print "-e s/"$1"/"$3"/"}'`<<<"`cat stuff.txt`"
123 jdy kdt
kdb 456 gbk
qng pbf 789
non non non
try one 123
Убедитесь, что ваша оболочка поддерживает столько параметров, сколько у вас на карте.
Ответ 4
Это довольно просто с Tcl:
set fA [open listA r]
set fB [open listB r]
set fin [open input.file r]
set fout [open output.file w]
# read listA and listB and create the mapping of corresponding lines
while {[gets $fA strA] != -1} {
set strB [gets $fB]
lappend map $strA $strB
}
# apply the mapping to the input file
puts $fout [string map $map [read $fin]]
# if the file is large, do it line by line instead
#while {[gets $fin line] != -1} {
# puts $fout [string map $map $line]
#}
close $fA
close $fB
close $fin
close $fout
file rename output.file input.file
Ответ 5
вы можете сделать это в bash
. Получите свои списки в массивы.
listA=(a b c)
listB=(d e f)
data=$(<file)
echo "${data//${listA[2]}/${listB[2]}}" #change the 3rd element. Redirect to file where necessary
Ответ 6
Используйте tr (1) (переводьте или удалите символы):
cat file | tr 'abc' 'XYZ' > file_new
mv file_new file