Изменить несмежные строки, указанные номерами строк

Я заставил свой адаптер работать, следуя этому руководству по ссылке

3
29.02.2020, 01:30
5 ответов

Если 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 не обрабатывают повторяющиеся номера строк. Все повторяющиеся номера строк обрабатываются только один раз.

2
28.04.2021, 23:21

Вы можете использовать саму sed (или другую утилиту обработки текста -по вашему выбору ), чтобы преобразовать номера строк в выражения sed, а затем передать их в sed с помощью переключателя -f

Пр.

sed 's:$:s/^/MARKER/:' linenos | sed -f- -i target_file

По крайней мере, sed вызывается только дважды .

10
28.04.2021, 23:21

С 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с ненулевым -значением.

6
28.04.2021, 23:21
$ 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
4
28.04.2021, 23:21

Другой способ, используя edвместо sedдля изменения target_fileвместо:

(while IFS= read n; do echo "${n}s/^/MARKER/"; done < linenos; echo w) |  ed -s target_file
1
28.04.2021, 23:21

Теги

Похожие вопросы