LINUX.ORG.RU
ФорумAdmin

Хитрый sed?

 , ,


0

2

Как с помощью однострочника изменить в файле /var/lib/dhcp3/dhcpd.leases последний lease для ip?

Пример файла:

...

lease 192.168.10.59 {

starts 6 2012/06/09 02:13:31;

ends 6 2012/06/09 02:43:31;

cltt 6 2012/06/09 02:13:31;

binding state active;

next binding state free;

hardware ethernet 00:00:06:a7:b6:fd;

uid «\001\000\300&\247\266\375»;

client-hostname «001-WS»;

}

lease 192.168.10.13 {

starts 6 2012/06/09 03:12:20;

ends 6 2012/06/09 03:42:20;

cltt 6 2012/06/09 03:12:20;

binding state active;

next binding state free;

hardware ethernet 00:11:11:11:8c:97;

uid «\001\000\021\021G\214\227»;

client-hostname «002-ws»;

}

lease 192.168.10.59 {

starts 6 2012/06/09 03:13:31;

ends 6 2012/06/09 03:43:31;

cltt 6 2012/06/09 03:13:31;

binding state active;

next binding state free;

hardware ethernet 00:00:06:a7:b6:fd;

uid «\001\000\300&\247\266\375»;

client-hostname «001-WS»;

}

...

Нужно заменить строчку строчку содержащую «ends» у последнего совпадения ip 192.168.10.59

Пока получилось показать и изменить последний lease для ip:

cat /var/lib/dhcp3/dhcpd.leases | grep «lease 192.168.10.75» -A9 | tail -n10 | sed «/ends/s/.*/ ends 6 `date '+%Y/%m/%d %T' | sed 's/\//\\\\\//g'`/»

заменить сам файл этим выводом не могу т.к. выводится только один ip

Можно заменить просто последнюю строку «ends 6 ...».

Проверяй:

Предполагается что все строки ends... уникальны или по крайней мере что замена тех нескольких, которые совпадают с последней, не приведет к негативным последствиям.

SRC=`cat dhcpd.leases | grep -e "^ *ends 6 " | tail -n 1 ` ; SRC="${SRC//\//\\/}" ; DST=`date '+%Y/%m/%d %T'` ; DST=`echo -n " ends 6 $DST;"` ; DST="${DST//\//\\/}" ; echo "'$SRC' -> '$DST'" ; cat dhcpd.leases | sed "s/$SRC/$DST/g"
Kroz ★★★★★ ()

Хм. Если будешь вывод в файл делать, убери вот это : echo «'$SRC' -> '$DST'»

Kroz ★★★★★ ()
let num=`cat /var/lib/dhcp3/dhcpd.leases | grep "lease 192.168.10.75" -c`
cat /var/lib/dhcp3/dhcpd.leases | grep "lease 192.168.10.75" -A9|sed ':a;N;$!ba;s/ends[ 0-9\/:;]*/ends 6 '"`date "+%Y\/%m\/%d %T"`/$num"
yozan ()

ИМХО, это проще решать не одним sed, а двумя. Сначала получить номер нужно строки, потом сделать замену в этой строке.

Вот так получаются все номера строк с ends для заданного ip-адреса:

cat -n /var/lib/dhcp3/dhcpd.leases | sed -n -e '/lease 192.168.10.59/,/}/s/ends .*//p'

Вот так получается номер последней строки, хотя можно предыдущую команду пропустить через tail или загрузить в массив bash'а:

cat -n /var/lib/dhcp3/dhcpd.leases | sed -n -e '/lease 192.168.10.59/,/}/{s|ends .*||;tb;ba;:b;x;:a;}' -e '$!d' -e '$x;p'

Ну, а по номеру строки (в bash переменной NUM) уже можно делать так:

sed «${NUM}s/.*/ends 6 `date '+%Y/%m/%d %T'`»

Только я не понял, зачем вам «sed 's/\//\\\\\//g'», все нужные слеши можно засунуть в команду date.

mky ★★★★★ ()
$ sed -rn '${H;g;s/^(ends ).*/\1ZZZZ/mp;b};/^lease/{x;p;x;h;b};H' t.txt

lease 192.168.10.59 {
starts 6 2012/06/09 02:13:31;
ends 6 2012/06/09 02:43:31;
cltt 6 2012/06/09 02:13:31;
binding state active;
next binding state free;
hardware ethernet 00:00:06:a7:b6:fd;
uid "\001\000\300&\247\266\375";
client-hostname "001-WS";
}
lease 192.168.10.13 {
starts 6 2012/06/09 03:12:20;
ends 6 2012/06/09 03:42:20;
cltt 6 2012/06/09 03:12:20;
binding state active;
next binding state free;
hardware ethernet 00:11:11:11:8c:97;
uid "\001\000\021\021G\214\227";
client-hostname "002-ws";
}
lease 192.168.10.59 {
starts 6 2012/06/09 03:13:31;
ends ZZZZ
cltt 6 2012/06/09 03:13:31;
binding state active;
next binding state free;
hardware ethernet 00:00:06:a7:b6:fd;
uid "\001\000\300&\247\266\375";
client-hostname "001-WS";
}

ищет последнюю lease, и меняет после неё ends.* на ends ZZZZZ

drBatty ★★ ()
Ответ на: комментарий от drBatty

Ваш sed делает не совсем то, что хотел ТС, он меняет ends последней lease в файле, а не последней для заданного ip-адреса. И даёт пустую строку в начале вывода. Но скрипт можно улучшить, я правда не понял, зачем конструкция:

«x;p;x;h;b»

почему не просто «x;p;b» ?

Другое дело, что судя по тому, что писал ТС, ему длинные команды в sed не понятны и врятли он будет с ними разбиратся. А подход с определеним номера нужной строки проще и он сможет в дальнейшем писать свои костыли не задавая каждый раз вопрос на ЛОРе по поводу сложной sed-команды.

mky ★★★★★ ()
Ответ на: комментарий от mky

Ваш sed делает не совсем то, что хотел ТС, он меняет ends последней lease в файле, а не последней для заданного ip-адреса.

ну поменяйте lease в скрипте на lease 1.2.3.4, не вижу проблемы. Или там есть такие хитрые lease без ends?

И даёт пустую строку в начале вывода.

I like it

я правда не понял, зачем конструкция: «x;p;x;h;b» почему не просто «x;p;b» ?

вот я буду часами думать об этом... В следующий раз напишу x;p;b...

Другое дело, что судя по тому, что писал ТС, ему длинные команды в sed не понятны и врятли он будет с ними разбиратся. А подход с определеним номера нужной строки проще и он сможет в дальнейшем писать свои костыли не задавая каждый раз вопрос на ЛОРе по поводу сложной sed-команды.

время покажет...

drBatty ★★ ()
Ответ на: комментарий от Pinkbyte

плюсую omshell

может покажешь как сбросить определенный ip

lioncub ★★ ()
Ответ на: комментарий от lioncub

Делаете копию файла, там всё меняете и записываете файл обратно. Всё равно, sed (даже с -i) сначала создаст изменённый файл, а потом запишет его обратно и если какой-то другой процесс одновременно с sed будет изменять /var/lib/dhcp3/dhcpd.leases результат будет неопределён.

mky ★★★★★ ()
Ответ на: комментарий от mky

ладно... пока буду тупо его убивать при изменениях.... пока руки не дошли... а так и на perl'e можно накатать быстренько (не однострочник)

спасибо!

lioncub ★★ ()
Ответ на: комментарий от lioncub

это потому, что у меня критерий ^ends, а у вас перед ends есть пробелы. Потому и не работает. fixed

$ sed -rn '${H;g;s/^(\s*ends ).*/\1ZZZZ/mp;b};/^lease/{x;p;x;h;b};H' dhcpd.leases.test | tail -n15
  next binding state free;
  hardware ethernet 00:1a:4d:21:9a:20;
  uid "\001\000\032M!\232 ";
  client-hostname "m1-ws";
}
lease 192.168.10.22 {
  starts 3 2012/06/13 01:40:20;
  ends ZZZZ
  cltt 3 2012/06/13 01:40:20;
  binding state active;
  next binding state free;
  hardware ethernet 00:1a:4d:21:9a:20;
  uid "\001\000\032M!\232 ";
  client-hostname "m4-ws";
}
\s* означает «любое количество пробельных символов»

drBatty ★★ ()
Ответ на: комментарий от mky

Всё равно, sed (даже с -i) сначала создаст изменённый файл, а потом запишет его обратно

нет. Sed создаст временный файл, а потом его _переименует_.

и если какой-то другой процесс одновременно с sed будет изменять /var/lib/dhcp3/dhcpd.leases результат будет неопределён.

Вполне определён. Операция переименования атомарна в рамках ФС, и потому никакой неоднозначности быть не может - либо sed переименует свою временную копию, а потом процесс, либо наоборот, в зависимости от того, что произойдёт раньше. Естественно, это в случае, если другой процесс работает так же как sed, что в большинстве случаев на практике наблюдается. (да, мы в Linux'е, и можем написать хитрый процесс, который плевал на правила. В этом случае _можно_ получить неопределённый результат. Не нужно мне это доказывать, я и сам в курсе. С дуру можно и член поломать.)

drBatty ★★ ()
Ответ на: комментарий от lioncub

ладно... пока буду тупо его убивать при изменениях.... пока руки не дошли... а так и на perl'e можно накатать быстренько (не однострочник)

на самом деле, у меня тут тоже не однострочник. Это только на ЛОРе я так выделываюсь, IRL сложные одностроки не нужны. Sed это тоже ЯП, и он попроще перловки будет...

drBatty ★★ ()
Ответ на: комментарий от drBatty

Давно это «либо-либо» стало означать определённый результат? Кот Шрёдингера «либо становится мёртвым, либо остаётся живым» — это тоже определёный результат?

Причём не надо тут играть в КО и приплетать правила работы процессов с файлами. В данном контексте определённость результата означает, будет ли решена задача ТС по требуемому ему изменению параметров лизы после выполнения скрипта или нет.

Судя под этому:

пока буду тупо его убивать при изменениях

ТС правильно понял, что я хотел сказать.

mky ★★★★★ ()
Ответ на: комментарий от drBatty

Кстати, получается, хоть это и не принято, что если делать не rename(), а open(O_TRUNC), write(), то сохраняются атрибуты файла (права доступа, SeLinux и т.д.).

Интерестно, есть аналог «chmod –reference», но только переносящий все атрибуты файла?

mky ★★★★★ ()
Ответ на: комментарий от mky

Давно это «либо-либо» стало означать определённый результат?

всегда. содержимое файла будет таким, которым его оставил последний процесс. Вот если-бы, как вы пишете,

а потом запишет его обратно

то тут да, возникает неопределённость - файл может и изменится _непредсказуемо_, если его _пишут_ два процесса. Фишка в том, что процесс _записи_ не является атомарным, в отличие от переименования. (правда можно избежать неоднозначности, если записывать _всегда_ в конец файла, и небольшими порциями. Так, как пишутся логи)

В данном контексте определённость результата означает, будет ли решена задача ТС по требуемому ему изменению параметров лизы после выполнения скрипта или нет.

ну сиё мне неведомо, потому я рассматриваю довольно общий случай, хотя и предполагаю, что другой процесс работает также, как и sed.

Кстати, получается, хоть это и не принято, что если делать не rename(), а open(O_TRUNC), write(), то сохраняются атрибуты файла (права доступа, SeLinux и т.д.).

именно так. И если мне нужно изменить содержимое файла, но при этом не затрагивать его атрибуты, я именно так и делаю. Естественно, такой процесс должен работать в одиночку.

drBatty ★★ ()
Ответ на: комментарий от drBatty

Под «запишет обратно» я не подразумевал open()+write(). Я знаю, что там используется rename() временного файла. Просто, когда я первый раз читал описание «sed -i», не помню где, там именно было использовано словосочетание «write back», как, допустим, здесь http://unstableme.blogspot.com/2010/01/sed-save-changes-to-same-file.html :

Today I am going to show how we can use sed to do some operation on a file ... and write back the results to the same file.

Видимо так это у меня в голове и засело.

Не знаю как под 2.6 и 3.x ядрами, но под 2.4 write() на файл на локальной ФС был атомарен, независимо от размера (хоть 500 Мбайт), другое дело, что fwrite() разбивается на кучу write() тогда и возникает мешанина. Может сейчас что и поменялось, а тогда получалось, что write() переводило процесс в uninterruptible sleep (D state) и процесс, производящий запись в этот же файл ждал пока закончится write() первого процесса. Правда, не помню, как вёл себя read(), пока выполялся write().

mky ★★★★★ ()
Ответ на: комментарий от mky

когда я первый раз читал описание «sed -i», не помню где, там именно было использовано словосочетание «write back», как, допустим, здесь http://unstableme.blogspot.com/2010/01/sed-save-changes-to-same-file.html :

я тоже был несколько удивлён тому, что на самом деле значит «in place»...

Не знаю как под 2.6 и 3.x ядрами, но под 2.4 write() на файл на локальной ФС был атомарен

я точно не помню, но, если мне не изменяет память, в 2.6+ атомарность гарантируется только при записи небольших кусков (512 байт вроде максимум), для бОльших кусков она тоже гарантируется, но вот насколько бОльших - зависит уже от реализации.

drBatty ★★ ()
Ответ на: комментарий от drBatty

512 байт это PIPE_BUF (его минимальный размер по POSIX). А вот writev() действительно атомарен под Линуксом, или я как-то не так понимаю man?

mky ★★★★★ ()

tac /var/lib/dhcp3/dhcpd.leases | grep -m 1 «lease» | sed 's/192.168.10.13/192.168.0.0/'

Так не проще?

Aligator ()
Ответ на: комментарий от mky

А вот writev() действительно атомарен под Линуксом, или я как-то не так понимаю man?

присоединяюсь к вопросу.

drBatty ★★ ()
Ответ на: комментарий от lioncub

судя по тому как работает dhcpd, то он именно в конец файла дописывает лизы...

ну да.

drBatty ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.