Если fileN
содержит номера строк, которые нужно изменить, а target_file
— это текстовый файл, содержащий строки, которые нужно изменить. Минимальное решение потребует чтения каждого файла один раз.
Если файл, содержащий номера строк, содержит одно число (больше 1 )в строке, отсортирован и нет повторений, мы можем использовать:
awk 'BEGIN{ getline lineN <"fileN"} {
if(NR==lineN){$0="MARKER " $0;getline lineN <"fileN"}
}1' target_file
Который будет хранить в памяти только одну строку (каждого файла )и проходить оба файла от начала до конца. Однако, как только awk обработает строку, например, строку 15, он не вернется, например, к строке 12. Таким образом, файл lineN
должен быть отсортирован (без повторения и иметь значение больше 1 ), чтобы это работало.
Конечно, наивное решение состоит в том, что файл номеров строк можно отсортировать sort -nu fileN
.
Но, если список номеров строк может быть несортированным (и повторяющимся ), мы можем использовать либо sed,ed
(предшественник sed
), либо awk (позже):
Преобразуйте каждую строку в lineN
в команду редактирования sed, например s/^/MARKER /
. Либо оболочка printf, либо sed могут сделать это :
printf '%ss/^/MARKER /\n' $(<fileN) | sed -f - target_file
sed 's#$#s/^/MARKER /#' fileN | sed -f - target_file
{ printf '%ss/^/MARKER /\n' $(<fileN); printf '%s\n',p Q; } | ed -Gs target_file
{ sed 's#$#s/^/MARKER /#' fileN ; echo "w" ; } | ed target_file
Обратите внимание, что в последнем случае редактирование выполняется непосредственно в исходном файле. Последняя команда w
записывает изменения в файл. Если необходимо распечатать результат, используйте третий вариант, который напечатает все строки.
В awk захватить все fileN
в памяти и обработатьtarget_file
awk '{ if(NR==FNR){
a[$1]=1
}else{
if(a[FNR]==1){ printf("%s","MARKER ")};
print
}
}' fileN target_file
Или с помощью переменной для управления окончанием списка файлов с номерами строк:
awk '{ if (dofile==1) { if(a[FNR]==1){ printf("%s","MARKER ")};
print
}else{
a[$1]=1
}
}' fileN fileK dofile=1 target_file
Обратите внимание, что последняя версия позволяет использовать несколько файлов с номерами строк, например fileN
и fileK
в примере.
Также обратите внимание, что версии awk не обрабатывают повторяющиеся номера строк. Все повторяющиеся номера строк обрабатываются только один раз.
Вы можете использовать саму sed (или другую утилиту обработки текста -по вашему выбору ), чтобы преобразовать номера строк в выражения sed, а затем передать их в sed с помощью переключателя -f
Пр.
sed 's:$:s/^/MARKER/:' linenos | sed -f- -i target_file
По крайней мере, sed вызывается только дважды .
С perl
(, где GNU sed
получил -i
от):
perl -pi -e '
BEGIN{$l{0+$_}=1 while <STDIN>}
$_ = "MARKER$_" if $l{$.}' target_file < linenos
Мы передаем список номеров строк на стандартный ввод perl
. Это читается в блоке BEGIN
.
Для каждой строки ввода мы преобразуем эту строку в число с помощью 0+$_
. Это убирает символ новой строки, а также канонизирует числа (все 1e0, 1, 01 становятся 1 ).
Хэш-таблица %l
заполняется значением 1
для каждого номера строки в качестве ключа.
target_file
обрабатывается в основном цикле -p
, где MARKERS
добавляется к строкам, где текущий номер строки($.
)находится в %l
с ненулевым -значением.
$ awk 'NR==FNR{a[$1]="MARKER"; next} {print a[FNR] $0}' linenos target_file
line one
MARKERline two
line three
line four
MARKERline five
line six
или для экономии памяти:
$ awk 'NR==FNR{a[$1]; next} {print (FNR in a ? "MARKER" : "") $0}' linenos target_file
line one
MARKERline two
line three
line four
MARKERline five
line six
Если вы хотите редактировать «на месте» (так же, как perl и GNU sed с -i
), используйте GNU awk и измените awk '...'
на awk -i inplace '...'
и добавьте print;
перед оператором next
, чтобы ваш linenos
файл не очищается. ИМХО проще сделать это с помощью любого awk (или любого другого инструмента UNIX):
awk 'script' linenos target_file > tmp && mv tmp target_file
Другой способ, используя ed
вместо sed
для изменения target_file
вместо:
(while IFS= read n; do echo "${n}s/^/MARKER/"; done < linenos; echo w) | ed -s target_file