Monday, January 26, 2009

Perl как замена sed, или perl однострочные скрипты.

Был занят, занимался самообразованием, т.к. почувствовал что погряз в одном и том же. Расширяю кругозор, узнаю новое, к чему то стремлюсь, это очень интересно и занимает очень много времени.

Теперь по делу.

Операционная система у меня ubuntu ++ постоянно по работе приходится сидеть в консоли. Очень часто возникает задача, быстро по шаблону поменять что то в файле. Ответ на вскидку = sed. В свое время я читал про него, но понял что при помощи perl могу делать то же самое. Прошло время, sed я забыл совершенно, а вот perl тоже стал забываться, и я постоянно не могу найти пример однострочных скриптов.

Теперь по порядку.

Задача:


Дано.
Есть файл xx.txt в котором лежат числа от 1-го до 5-ти, каждое число, новая строка.


cd@laptop:~/data/tmp$ cat xx.xx
1
2
3
4
5


Нужно:

Хочется заменить все вхождения строки 3 на что то свое, пусть это будет строка "hello"


Решение:

cd@laptop:~/data/tmp$ perl -ig -ne 's/3/hello/;print $_; ' xx.xx
cd@laptop:~/data/tmp$ cat xx.xx
1
2
hello
4
5


Или


cd@laptop:~/data/tmp$ perl -ig -pe 's/3/hello/; ' xx.xx
cd@laptop:~/data/tmp$ cat xx.xx
1
2
hello
4
5
cd@cd-acer:~/data/tmp$ ls *g
9.jpg xx.xxg




Объяснение.

perl позволяет запускать однострочные скрипты, не создавая файл с кодом скрипта.

Пример:

cd@laptop:~/data/tmp$ perl -e 'print "test\n"'
test
cd@laptop:~/data/tmp$



За это отвечает опция -e после которой идет код для выполнения.

Однострочные скрипты, после кода, могут принимать параметром файл, а также, можно заставить выполнять код указанный -e для каждой строки.

Пример:

cd@laptop:~/data/tmp$ perl -ne "print ;" xx.xx
1
2
hello
4
5


Т.е. в данном случае мы попросили перл, выполнить распечатку строки по умолчанию, а т.к. была указана опция -n то это команда применялась к каждой строке файла xx.xx, т.е. получили cat

Также существует параметр -p, который после выполнения кода распечатывает строку. Т.е. логично что команда вида


perl -ne "dosmth; print $_;" file


будет аналогична

perl -pe "dosmth" file


Ну а теперь завершая. Мы добились того, что можем выполнять любые операции над каждой строкой в файле, и печатать это в stdout, однако первоначальная постановка задачи, звучала как замена строк в файле на лету. Именно для этого и существует опция -i

т.е.


perl -i -pe "dosmth" file

выполнит код dosmth для отдельно взятой строки и заменит ее в указанном файле.

опция -i может принимать необязательный параметр, и тогда, скрипт перед началом работы сделает backup копию файла

Пример.


cd@laptop:~/data/tmp/1$ perl -ibak -ne 'print;' 1.txt
cd@laptop:~/data/tmp/1$ ls
1.txt 1.txtbak
cd@laptop:~/data/tmp/1$ cat 1.txt
1
2
3
cdlaptop:~/data/tmp/1$ cat 1.txtbak
1
2
3
cd@laptop:~/data/tmp/1$


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


Upd:



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




Возьмем пример файла конфига



cd:$ cat config
Key1 Value2
build=projectname.005
one more line



Допустим нам нужно получить такой же конфиг но с build=projectnam.006



perl -i -pe 's/(projectname\.)(\d+)/sprintf("%s%03d", $1, $2+1)/e' config

Пояснения.

-i -pe объяснялось выше.

Что изменилось? В конце регулярного выражения добавился флаг e, который означает что правая часть выражения будет выполняться как код perl и только после выполнения результат подставится для замены.




Можно было бы конечно сделать что то вроде


perl -pe 's/(projectname\.)(\d+)/$1 . ($2+1)/e' config
Key1 Value2
build=projectname.6
one more line

! опция -i была убрана, я хотел чтобы файл не менялся, а результат был в output



Как видим пропадают два ведущих нуля, именно поэтому была использована функция sprintf

4 comments:

Анастасия said...

Спасибо. Пригодилось :)
Если нет возражений, я дала на Ваш блог ссылку в своем http://aal-blog.blogspot.com/2009/10/stroka1-stroka2.html

Unknown said...

Без проблем. Рад хоть что то пригодилось.

Anonymous said...

Есть вопрос...
Возможно ли запуская скрипт дать ему на обработку данные.
Например запускаю скрипт совмещения двух баз там идёт запрос месяцев... потом передача вывода на печать ...
тоесть хотелось бы чтоб данные поступали в скрипт с запуском...
Заранее спасибо

Unknown said...

D *nix точно можно, насчет винды не знаю.

Возьмем пример из статьи

cd@cd-acer:/tmp$ perl -ig -ne 's/3/hello/;print $_; ' xx.xx

В данном случае однострочный скрипт натравлен на файл, вам же хочется натравить скрипт на STDOUT вывод какой то команды.

Возьмем для примера команду

$cat 1.txt
1
2
3
4
5

cat печатает файл в STDOUT, теперь натравливаем наш скрипт на вывод этой команды через pipe

cd@cd-acer:/tmp$ cat 1.txt | perl -ig -ne 's/3/hello/;print $_; '
1
2
hello
4
5

Почитайте http://ru.wikipedia.org/wiki/Перенаправление_(UNIX)#.D0.9A.D0.BE.D0.BD.D0.B2.D0.B5.D0.B9.D0.B5.D1.80.D1.8B

 
Каталог сайтов, Добавить сайт