Intereting Posts
Возможно ли совместное использование внешнего жесткого диска через сервер? Противоположное поведение keepalive (обратный прокси nginx на ElasticSearch) / bin / bash Нет такого файла или каталога Соединение закрыто Как я могу узнать, какие флаги компоновщика необходимы для использования данной функции библиотеки C? Рекурсивно найти все файлы с именем «file.txt» и выполнить команду sed Arch Linux: python и python2 находятся в конфликте плохая геометрия: количество блоков 967424 превышает размер устройства (415232 блока) Bash: `test` находит файл, но` source` wont Ввод нового / графического редактора в список редакторов Используется xrandr для поворота монитора, но он не может отменить его после удаления скрипта Инструмент lshw и возвращаемые параметры и их значения Ли Linux использует файлы устройств для разделов жесткого диска для доступа к жесткому диску? Как я могу увидеть, использовались ли termcap или terminfo и как их менять? Инициируйте другой терминал, запускающий tmux (от терминала, запускающего tmux) Почему Unix имеет кольцевую архитектуру, почему не окна?

Как использовать команду sed для изменения записи файла в строке в сценарии оболочки?

Это мой file1 :

  # $ BQ
 # {VOL ​​@home}
 #database daba
 #relation tcdeatid
 #copy 1
 # {version 0}
 #opendb
 #Чисто
 # .lruno: = 72
 # .infno: = 1
 # .tid.noel: = 101
 # .tid.info: = 64
 # .tid.setnr: = 1225 <--- (номер 1225 будет изменен)
 # .typeidm: = 1
 # .sourcetable: = 2
 #writedb     
 #Чисто
 # .lruno: = 72
 # .infno: = 205
 # .tid.noel: = 101
 # .tid.info: = 76
 # .tid.setnr: = 5625 <--- (номер 5625 будет изменен)
 # .typeidm: = 1
 # .sourcetable: = 2
 #writedb
 # $ EOJ 

В file2 меня есть правильные записи:

  # .tid.info: = 3345 <--- (Я хочу поместить этот номер в файл1 вместо 64)
 # .tid.setnr: = 1254 <--- (Я хочу поместить это число в файл1 вместо 1225)
 # .tid.info: = 5567 <--- (Я хочу поместить этот номер в файл1 вместо 76)
 # .tid.setnr: = 9056 <--- (Я хочу поместить это число в файл1 вместо 5625)

Я хочу внести изменения в file1 , направляемый данными из file2 .

Я попробовал sed "s/tid.setnr := 1225/tid.setnr := 1254/g" file1 > modified_file1

Но вместо того, чтобы делать это вручную, я хочу, чтобы мой скрипт прочитал новые значения tid.info и tid.setnr из tid.info и tid.setnr соответствующие строки в file1 . Первая строка в file2 должна заменить всю первую строку в tid.info , содержащую tid.info , вторая строка в file2 должна заменить вторую строку в tid.setnr , содержащую tid.setnr , и так далее.

Один из способов сделать это с помощью sed – создать сценарий sed :

 tmpfile=/tmp/Nainita.$$ exec 3< file2 grep -n "tid\.setnr" file1 | while IFS=: read n junk do IFS= read -r line <&3 || break printf "%sc%s\n%s\n" "$n" '\' "$line" done > "$tmpfile" exec 3<&- sed -f "$tmpfile" file1 > modified_file1 rm -f "$tmpfile" 
  • exec 3< file2 открывает file2 для чтения в дескрипторе файла 3.
  • grep -n находит строки в tid.setnr , содержащие tid.setnr , по номеру строки. Это попадает в цикл while.
  • while IFS=: read n junk

    • while … read … означает чтение строки за раз, несколько раз, пока есть информация для чтения (т. е. остановка, когда она достигает конца данных).
    • IFS=: read n junk означает читать все до первого : (который будет номером строки из grep -n ) в n , а остальная часть строки (старая tid.setnr данных tid.setnr ) в junk , которую мы игнорируем , потому что нас это не волнует.
  • IFS= read -r line <&3 читает строку из файлового дескриптора 3 ( file2 ), делая все, что мы знаем, как сделать, чтобы заставить оболочку не калечить ее и помещать в переменную, называемую line .
  • … || break … || break говорит, что если вышеприведенное read завершилось неудачно (предположительно из-за конца файла), вырваться из цикла.
  • printf записывает команду sed c hange, адресованную номеру строки n , говоря, что эта строка должна быть заменена содержимым line .
  • done > "$tmpfile" отмечает конец цикла while и указывает, что стандартный вывод всего цикла равен $tmpfile . Это будет выглядеть следующим образом:

      13с \
     # .tid.setnr: = 1254
     22с \
     # .tid.setnr: = 9056 
    • Обратите внимание, что цикл while завершается, как только он получает EOF с любого входа. Поэтому, если file1 имеет больше tid.setnr строк, чем file2 , дополнительные будут оставаться неизменными (т. tid.setnr Команды c для них не будут сгенерированы). Аналогично, если file2 имеет больше строк tid.setnr чем file1 , дополнительные будут игнорироваться. К сожалению, это несоответствие не сообщается, хотя добавление этой возможности было бы не очень сложно.
  • exec 3<&- закрывает дескриптор файла 3.

  • sed -f "$tmpfile" file1 > modified_file1 запускает sed на file1 , считывая команды из $tmpfile и записывая вывод в файл modified_file1 .

Это должно делать то, что вы хотите. Очевидно, измените имена файлов, если хотите. Вы должны запустить этот «как есть» один раз, а затем посмотреть файл modified_file1 (или просто изменить команду sed чтобы не перенаправлять свой вывод, поэтому он записывает на экран) и убедитесь, что результат – это то, что вы хотите. Затем вы можете изменить команду sed на sed -i чтобы отредактировать file1 на месте (если это то, что вы хотите сделать).

Если команда sed дает ошибку типа «Слишком много номеров строк», попробуйте $tmpfile файл сценария ( $tmpfile ) на более мелкие файлы. Я предлагаю начать с размера менее 100 команд; например, 90 команд, которые составляют 180 строк, поскольку каждая команда состоит из двух строк. Вы можете сделать это вручную, с помощью текстового редактора 1 , но есть инструмент, специально предназначенный для этой работы. Это, интуитивно, называется split . Команда

 split --lines=180 "$tmpfile" 

разделит скрипт на файлы в текущем каталоге с именем xaa , xac , xac , …. Первый n -1 будет длиной 180 строк; последний будет тем, чем он должен быть (≤ 180), чтобы составить общее количество. Например, если у вас есть 500 экземпляров tid.setnr , тогда ваш скрипт будет длиной 1000 строк, и вы получите шесть x файлов – xaa , xac , xad , xae и xae будут иметь 180 строк, а xaf будет иметь 100. Теперь сделайте

 sed -f xaa file1 > modified_file1aa 

Если вы все равно получите «Слишком много номеров строк», вернитесь назад и повторите попытку с меньшим количеством --lines . Если он не сообщает об ошибке, посмотрите на modified_file1aa и посмотрите, похоже ли, что были изменены первые 90 строк tid.setnr . Если все будет хорошо, тогда сделайте

 sed -f xab modified_file1aa > modified_file1ab sed -f xac modified_file1ab > modified_file1ac sed -f xad modified_file1ac > modified_file1ad sed -f xae modified_file1ad > modified_file1ae sed -f xaf modified_file1ae > modified_file1af 

modified_file1af теперь является вашим окончательным modified_file1 .

Если вы хотите играть в эксперимент, вы можете

  • Попробуйте увеличить количество --lines до --lines пор, пока вы не узнаете, что такое максимум.
  • Пытаться

     sed -f xaa -f xab -f xac -f xad -f xae -f xaf file1 > modified_file1_test 

    но это, вероятно, не сработает.

  • Если вы сделаете вышеуказанные эксперименты, я рекомендую вам рассказать нам о результатах.

Если вам нужно сделать это только один раз, все готово. Но если вам нужно сделать это несколько раз, измените последние две строки кода в верхней части этого ответа на

  split --lines = 180 "$ tmpfile" <--- (Использование числа, которое работает для вас, конечно)
 cp file1 modified_file1
 для f в x *
 делать
         sed -i -f "$ f" modified_file1
 сделанный
 rm -f "$ tmpfile" x * 

Как я упоминал ранее, опция -i указывает sed редактировать указанный файл на месте (т. Е. Записывать изменения обратно во входной файл). Или, еще проще,

 split --lines=180 "$tmpfile" for f in x* do sed -i -f "$f" file1 done rm -f "$tmpfile" x* 

если вам не нужно сохранять исходный file1 без изменений.

Обратите внимание, что краткий обзор использования для split

  split [ OPTION ] ... [ INPUT [ PREFIX ]] 

и его поведение по умолчанию заключается в создании файлов под названием PREFIX aa , PREFIX ab , PREFIX ac и т. д. Другими словами, PREFIX по умолчанию – x . Если у вас могут быть другие файлы, имена которых начинаются с x , вы должны определить prefix как нечто уникальное (например, prefix=Nainita.$$. Или prefix=/tmp/Nainita.$$. ), А затем изменить выше в

 split --lines=180 "$tmpfile" "$prefix" for f in "$prefix"* do sed -i -f "$f" file1 done rm -f "$tmpfile" "$prefix"* 

______
1 Если вы обжора для наказания, вы можете сделать это с помощью sed – но зачем играть с огнем?

С командой RU GNU sed:

 sed -e 's/^#.tid.setnr :=.*//;tA;b;:A;R file2' -e 'd' file1 
 { sed '/^#\.tid\.setnr/!d;=;g;G' | paste -d'c\\\n' - - - ./addfile } <./mainfile | sed -f - ./mainfile 

Он просто добавляет соответствующие номера строк из ./mainfile в каждую строку в ./addfile как разделенную командной строкой c\ и <newline> перед передачей результатов в другое sed как скрипт для редактирования ./mainfile . Учитывая ваши данные примера, созданный скрипт выглядит так:

 13c\ #.tid.setnr := 1254 <--- (I want to put this number in file1 in place of 1225) 22c\ #.tid.setnr := 9056 <--- (I want to put this number in file1 in place of 5625) 

… который инструктирует sed чтобы выстроить линии 13 и 22 его вывода в соответствии с прилагаемыми строками для каждой команды.

Я нашел perl. Этот сценарий является грубым, который, кажется, решает проблему:

 #!/usr/bin/perl # use strict; # subroutine to load a test file into an array sub load{ my $file = shift; open my $in, "<", $file or die "unable to open $file : $!"; my @data = <$in>; chomp @data; foreach (@data) { s/\cM//g;} return @data; } my $file1 = shift || die "usage: $0 [file1] [file2]\n"; my @file1_data = &load($file1); my $file2 = shift || die "usage: $0 [file1] [file2]\n"; my @file2_data = &load($file2); my $i = 0; foreach( @file1_data ) { if( $i < @file2_data ) { my @s = split / /, $file2_data[$i]; if( @s ) { if( /^$s[0]/ ) { $_ = $file2_data[$i++]; } } } print "$_\n";