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

На FreeBSD 11 для ARMv6 (Raspberry Pi) мне не удалось заставить директиву crontab @reboot работать для пользователя root , а метод «добавить скрипты в файл /etc/rc.local » был заменен механизмом, который автоматически запускает скрипты, расположенные в /usr/local/etc/rc.d/ .

Другими словами, чтобы запустить сценарий при загрузке FreeBSD 11 (или 10?) И, вероятно, новее, поместите исполняемый сценарий оболочки с расширением .sh в эту папку, чтобы он запускался при загрузке. :

/usr/local/etc/rc.d/

Возможно, вам потребуется создать папку rc.d , если она не существует.

См. http://www.defcon1.org/html/rc-local.html для получения подробной информации.

4
03.01.2019, 21:19
6 ответов

Это изменит IP-адрес, связанный с хостом c, на 1.2.3.4:

.
$ sed 's/^ip/\nip/' file | perl -00pe 'if(/\nhost=c\n/){s/ip=\S+/ip=1.2.3.4/} s/\n\n/\n/' 
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=1.2.3.4
mask=255.0.0.0
host=c
ip=x.x.x.x
blahblah
mask=255.0.0.0
host=d

Пояснение:

  • sed 's/^ip/\nip/' file:добавьте дополнительную новую строку(\n)к каждой строке, начинающейся с ip. Я думаю, что это может работать не со всеми реализациями sed, поэтому, если ваша не поддерживает это, замените команду sedна perl -pe 's/^ip/\nip/'. Это нужно для того, чтобы использовать «режим абзаца» Perl (, показанный ниже ).

  • perl -00pe:-00заставляет Perl работать в «режиме абзаца», где «строка» определяется двумя последовательными символами новой строки. Это позволяет нам рассматривать каждый блок хоста как одну «линию». -peозначает «печатать каждую строку после применения к ней сценария, заданного -e».

  • if(/\nhost=c\n/){s/ip=\S+/ip=1.2.3.4/}:если эта «строка» (раздел )соответствует новой строке, за которой следует строка host=c, а затем еще одна новая строка, то замените ip=и 1 или более не -пробельных символов(\S+)после нее с ip=1.2.3.4.

  • s/\n\n/\n/замените каждую пару новых строк одной новой строкой, чтобы вернуть исходный формат файла.

Если вы хотите изменить файл на месте, вы можете использовать:

tmp=$(mktemp); sed 's/^ip/\nip/' file > $tmp; 
perl -00pe 'if(/\nhost=c\n/){s/ip=\S+/ip=1.2.3.4/} s/\n\n/\n/' $tmp > file
3
27.01.2020, 20:46

Я знаю, ты ждешь чего-то очень быстрого,и просто, как однострочная команда sed -или умный код awk, но если вам все равно...

#!/bin/bash
#Note: Adjusted to run with a posix shell (tested in dash)
filename='file'
newip='127.0.0.1'
hostchar='d'

tac "$filename" | while IFS= read -r line ; do 
     case $line in 
          host=${hostchar})  
              flag=on 
          ;;
          host=*)
              flag=off
          ;;
     esac
     if [ "$flag" = "on" ]; then
     case $line in 
          ip=*) 
              echo ip=$newip
              continue 
          ;; 
          #you can replace more variables at once by adding it here
          #in the same standard. 
          #for ex: mask=*) echo mask=$newmask; continue ;; etc...
     esac  
     fi
     echo $line 
done | tac 

Результаты:

ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=x.x.x.c
mask=255.0.0.0
host=c
ip=127.0.0.1
blahblah
mask=255.0.0.0
host=d
3
27.01.2020, 20:46

Это становится намного проще, если вы просматриваете файл в обратном порядке. К счастью, вы можете легко сделать это с помощью tac(, который оказывается catнаоборот ). Затем мы можем использовать относительно простой скрипт awkдля поиска вашего hostи изменить только егоip:

$ tac input | awk -v OFS="=" -v myip="changed_address" -v myhost="d" -F"=" '$1 == "host" { if( $2 == myhost ) { sw = "on" } else { sw="off" } } sw == "on" && $1 == "ip" { $2=myip } { print $0 }' | tac
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=x.x.x.c
mask=255.0.0.0
host=c
ip=changed_address
blahblah
mask=255.0.0.0
host=d

Я подробно объясню, как работает awk:

Во-первых, мы объявляем несколько переменных :по одной для hostи новое значение для ip:

.
-v myip="changed_address" -v myhost="d"

Далее объявляем разделитель полей для ввода и вывода:

-v OFS="=" -F"="

Теперь собственно сам awkскрипт:

$1 == "host" {             // If we see the "host" line..
  if( $2 == myhost ) {     // And it matches the one we're looking for..
    sw = "on"              // Set a flag to swap the next IP
  } else { 
    sw="off"               // Otherwise, unset the flag
  }
} 

sw == "on" && $1 == "ip" { // If the flag is set and this is an IP line.. 
    $2=myip                // Swap in the new IP 
}

{
   print $0                // Finally, print out the processed line
}

Как только все это будет сделано, мы просто используем tacеще раз, чтобы повторно -перевернуть его, снова сделав его вперед.

4
27.01.2020, 20:46

Другой вариант — отправить несколько команд наed:

h=c
ed -s file <<< $'/^host='"${h}"$'$\n?^ip=\nc\nip=new.ip.here\n.\nw\nq' > /dev/null

Основная идея состоит в том, чтобы отредактировать файл и конвейер в разделенном новой строке -списке команд (в двух строках кавычек ANSI C $'... '), которые ищут данный хост (в переменной $h), затем выполните поиск строки, начинающейся с ip=, затем измените эту строку на что-то новое, затем сохраните и закройте ed. Разделенные символами новой строки (\n), команды:

  1. /^host=...$h$--начать поиск(/)строки host=в начале строки (^), затем следует содержимое $hи конец строки($). Ослабьте требования к концу строки -из -, если ваш хост не полностью совпадает.

  2. ?^ip=--Теперь, когда мы находимся на совпадающей строке, найдите текст ip=в начале строки в обратном направлении.

  3. c--изменить эту строку

  4. вставить текстip=new.ip.here

  5. .--конец текста вставки

  6. w--записать файл на диск

  7. q--уволился

Вызов ed с -sотключает счетчик байтов -, который выдается при открытии и сохранении файла. Перенаправление всей команды на /dev/nullотключает вывод по умолчанию, когда edнаходит строки host=и ip=, которые мы ищем.

3
27.01.2020, 20:46
$ awk -v host=c -v newip=zzz.zzz.zzz.zzz '$0 ~ "^host=" host "$" { print; getline; $0 = sprintf("ip=%s\s", newip) }; 1' file
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=x.x.x.c
mask=255.0.0.0
host=c
ip=zzz.zzz.zzz.zzzs
blahblah
mask=255.0.0.0
host=d

Предполагается, что вы хотите изменить IP-адрес некоторого именованного хоста и что строка IP-адреса всегда находится после строки host=для этого хоста.

Программа awkберет имя хоста и новый IP-адрес в командной строке, устанавливая две переменные awkhostи newip. Затем код находит строку host=, соответствующую заданному имени хоста, считывает и отбрасывает следующую строку (, строку ip=, строку )и создает новую строку ip=с новым IP-адресом. Данные (изменены или нет )выводятся завершающим 1в программе.

1
27.01.2020, 20:46

Относительно короткое решение с использованием утверждений отрицательного просмотра вперед в регулярном выражении perl:

perl -p0e 's/(ip=)[^\n]*((?:(?!ip=).)*?host=c)/\1new_ip_for_host_c\2/gs' hosts.txt

Пояснение:

Сначала используется цикл печати -с использованием опции -p. Это перебирает строки файла, предоставленного в качестве аргумента для perl, и печатает их. Если строка изменена, печатается измененный вариант,как с sed.

Содержимое файла на самом деле не обрабатывается как отдельные строки, опция -pпросто служит для того, чтобы заставить perlсразу читать файл и использовать содержимое в качестве строки по умолчанию для сопоставления без использования переменной. Это достигается с помощью параметра -0, который заставляет perlобрабатывать многострочный ввод как одну строку, заменяя символы новой строки нулевыми байтами.

В шаблоне сохраняются части, за исключением изменяемого значения.

Значение распознается как любое до следующей новой строки, которая, как я предполагаю, понимается как следующий нулевой -байт в этой ситуации. Кто-то, кто знает это, возможно, может объяснить.

Затем, используя утверждение отрицательного просмотра вперед, сопоставляются все символы, которым не предшествует часть ip=, пока не встретится часть host=d. Это необходимо, иначе perl предпочел бы наиболее левое -совпадение кратчайшему и сопоставлялся бы с самой первой ip=до требуемой host=cчасти.

Символы, которым не предшествует ip=, сопоставляются не -жадным образом, в противном случае, опять же, совпадение будет охватывать все, начиная с первого ip=до host=c, поскольку по умолчанию предпочтение отдается самому длинному совпадению.. Другими словами, это гарантирует, что текст сопоставляется в разделах, заканчивающихся host=cи начинающихся сегментом ip=, ближайшим к host=c, а не самым дальним.

Символ, ограниченный отрицательным опережающим выражением внутри конструкции (?:). Он обозначает не захватывающую группу -, что означает, что она не будет учитываться в общем количестве обратных ссылок в строке подстановки. Группировка используется для того, чтобы знак *мог правильно количественно оценить всю комбинацию символов с отрицательным опережением -как один атом. В примере обратные ссылки варьируются от \1-\2, в то время как без захватывающей группы, не являющейся -, они будут \1-\3, где \3обозначает вложенную группу.

Обратные ссылки \1и \2в строке подстановки — это совпадающие части раздела, которые точно содержат IP-адрес хоста, найденного в совпавшем разделе.

Таким образом, IP-адрес будет заменен на new_ip_for_host_c.

Общее блаблабла:

Я не уверен, что формат данных был выбран ОП, просто выкладывая это в общем :лучше хранить такие простые данные таким образом, чтобы записи разделялись символами новой строки, т.е. не было многострочных записей. Обратитесь к формату /etc/passwd. Большинство стандартных фильтров Unix ориентированы на строку -, и манипулирование данными становится намного проще, если вы следуете "da Unix wae". Если ваши данные являются более сложными (, вложенными элементами данных и т. д. ), вам лучше использовать такой формат, как JSON, и манипулировать им с помощью соответствующего синтаксического анализатора.

1
27.01.2020, 20:46

Теги

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