Какой ваш последний полезный Perl однострочный (или труба с участием Perl)?
Однострочник должен:
- решить реальную проблему
- не быть сильно загадочным (должно быть легко понять и воспроизвести)
- стоить времени, которое требуется, чтобы написать это (не должно быть слишком умным)
Я ищу практические советы и приемы (дополнительные примеры для perldoc perlrun
).
24 ответа
Все однострочники из ответов собраны в одном месте:
perl -pe's/([\d.]+)/localtime $1/e;' access.log
ack $(ls t/lib/TestPM/|awk -F'.' '{print $1}'|xargs perl -e 'print join "|" => @ARGV') aggtests/ t -l
perl -e'while(<*.avi>) { s/avi$/srt/; rename <*.srt>, $_ }'
find . -name '*.whatever' | perl -lne unlink
tail -F /var/log/squid/access.log | perl -ane 'BEGIN{$|++} $F[6] =~ m{\Qrad.live.com/ADSAdClient31.dll} && printf "%02d:%02d:%02d %15s %9d\n", sub{reverse @_[0..2]}->(localtime $F[0]), @F[2,4]'
export PATH=$(perl -F: -ane'print join q/:/, grep { !$c{$_}++ } @F'<<<$PATH)
alias e2d="perl -le \"print scalar(localtime($ARGV[0]));\""
perl -ple '$_=eval'
perl -00 -ne 'print sort split /^/'
perl -pe'1while+s/\t/" "x(8-pos()%8)/e'
tail -f log | perl -ne '$s=time() unless $s; $n=time(); $d=$n-$s; if ($d>=2) { print qq ($. lines in last $d secs, rate ),$./$d,qq(\n); $. =0; $s=$n; }'
perl -MFile::Spec -e 'print join(qq(\n),File::Spec->path).qq(\n)'
Смотрите соответствующие ответы для их описания.
Пожалуйста, смотрите мои слайды для "Полевого руководства по параметрам командной строки Perl".
Лог-файлы Squid. Они великолепны, не так ли? За исключением того, что по умолчанию у них есть секунды с начала эпохи в качестве поля времени. Вот одна строка, которая читает из файла журнала squid и преобразует время в удобочитаемую дату:
perl -pe's/([\d.]+)/localtime $1/e;' access.log
С небольшой настройкой вы можете настроить отображение только строк с интересующим вас ключевым словом. Следующие наблюдения за stackru.com осуществляют доступ и распечатывают только эти строки с удобочитаемой датой. Чтобы сделать его более полезным, я даю вывод tail -f
, поэтому я могу видеть доступ в режиме реального времени:
tail -f access.log | perl -ne's/([\d.]+)/localtime $1/e,print if /stackru\.com/'
Вы можете не думать об этом как о Perl, но я использую ack неукоснительно (это разумная замена grep, написанная на Perl), и это позволяет мне редактировать, например, все мои тесты Perl, которые обращаются к определенной части нашего API:
vim $(ack --perl -l 'api/v1/episode' t)
В качестве примечания, если вы используете vim, вы можете запустить все тесты в буферах вашего редактора.
Для чего-то с более очевидным (хотя и простым) Perl мне нужно было узнать, сколько тестовых программ использовало тестовые фикстуры в каталоге t/lib/TestPM (для ясности я сократил команду).
ack $(ls t/lib/TestPM/|awk -F'.' '{print $1}'|xargs perl -e 'print join "|" => @ARGV') aggtests/ t -l
Обратите внимание, как "join" превращает результаты в регулярное выражение для подачи в ack.
Общая идиома использования find ... -exec rm {} \;
удалить набор файлов где-то в дереве каталогов не особенно эффективно, поскольку он выполняет rm
Команда один раз для каждого найденного файла. Одна из моих привычек, появившаяся в те дни, когда компьютеры не были такими быстрыми (дагнаббит!), - это заменить множество звонков на rm
с одним вызовом Perl:
find . -name '*.whatever' | perl -lne unlink
perl
часть командной строки читает список файлов, испускаемых * find
по одному на строку, отключает перевод строки и удаляет файл с помощью встроенного в Perl unlink()
функция, которая принимает $_
в качестве аргумента, если не указан явный аргумент. ($_
устанавливается на каждую строку ввода благодаря -n
флаг.) (* В наши дни большинство find
команды делают -print
по умолчанию, так что я могу пропустить эту часть.)
Мне нравится эта идиома не только из-за эффективности (возможно, менее важной в наши дни), но также из-за того, что в ней меньше чёрных / неудобных клавиш, чем при наборе традиционных -exec rm {} \;
последовательность. Это также позволяет избежать проблем с цитированием, вызванных именами файлов с пробелами, кавычками и т. Д., Которых у меня много. (Более надежная версия может использовать find
"s -print0
вариант, а затем спросите perl
читать разделенные нулями записи вместо строк, но я, как правило, довольно уверен, что имена моих файлов не содержат встроенных символов новой строки.)
Проблема: медиаплеер не загружает автоматически субтитры из-за того, что их имена отличаются от соответствующих видеофайлов.
Решение: переименуйте все *.srt (файлы с субтитрами), чтобы они соответствовали *.avi (файлы с видео).
perl -e'while(<*.avi>) { s/avi$/srt/; rename <*.srt>, $_ }'
ПРЕДУПРЕЖДЕНИЕ. Порядок сортировки оригинальных видеофайлов и имен субтитров должен быть одинаковым.
Вот более подробная версия вышеупомянутой однострочной:
my @avi = glob('*.avi');
my @srt = glob('*.srt');
for my $i (0..$#avi)
{
my $video_filename = $avi[$i];
$video_filename =~ s/avi$/srt/; # 'movie1.avi' -> 'movie1.srt'
my $subtitle_filename = $srt[$i]; # 'film1.srt'
rename($subtitle_filename, $video_filename); # 'film1.srt' -> 'movie1.srt'
}
Perl one-liner, который я использую больше всего, это калькулятор Perl
perl -ple '$_=eval'
Одна из самых больших проблем с пропускной способностью на $work - это загрузка веб-рекламы, так что я смотрю на низко висящие фрукты, ждущие, чтобы их собрали. Я избавился от рекламы Google, теперь у меня в поле зрения Microsoft. Итак, я запускаю хвост в файле журнала и выбираю интересующие строки:
tail -F /var/log/squid/access.log | \
perl -ane 'BEGIN{$|++} $F[6] =~ m{\Qrad.live.com/ADSAdClient31.dll}
&& printf "%02d:%02d:%02d %15s %9d\n",
sub{reverse @_[0..2]}->(localtime $F[0]), @F[2,4]'
Канал Perl должен начать с установки для autoflush значения true, чтобы все, что было применено, было немедленно распечатано. В противном случае выходные данные распределяются по частям, и каждый получает пакет строк после заполнения выходного буфера. Ключ -a разделяет каждую строку ввода на пустое пространство и сохраняет результаты в массиве @F (функциональность, основанная на способности awk разбивать входные записи на переменные $1, $2, $3...).
Он проверяет, содержит ли 7-е поле в строке URI, который мы ищем (используя \Q, чтобы избавить нас от необходимости избегать неинтересных метасимволов). Если совпадение найдено, оно печатает время, IP-адрес источника и количество байтов, возвращенных с удаленного сайта.
Время получается, принимая время эпохи в первом поле и используя "местное время", чтобы разбить его на составляющие (час, минута, секунда, день, месяц, год). Он берет часть первых трех элементов возвратов, секунды, минуты и часа, и меняет порядок, чтобы получить часы, минуты и секунды. Он возвращается в виде массива из трех элементов вместе с фрагментом третьего (IP-адрес) и пятого (размер) исходного массива @F. Эти пять аргументов передаются в sprintf, которая форматирует результаты.
@доктор Пеппер
Удалить буквальные дубликаты в $PATH
:
$ export PATH=$(perl -F: -ane'print join q/:/, grep { !$c{$_}++ } @F'<<<$PATH)
Печать уникальных чистых путей из %PATH%
переменная окружения (не касается ../
и так, заменить File::Spec->rel2abs
от Cwd::realpath
если это желательно) Быть портативным не однострочник:
#!/usr/bin/perl -w
use File::Spec;
$, = "\n";
print grep { !$count{$_}++ }
map { File::Spec->rel2abs($_) }
File::Spec->path;
В ответ на комбинацию Vim / Ack Ovids:
Я тоже часто что-то ищу, а потом хочу открыть соответствующие файлы в Vim, поэтому некоторое время назад я сделал себе небольшой ярлык (думаю, работает только в ZSH):
function vimify-eval; {
if [[ ! -z "$BUFFER" ]]; then
if [[ $BUFFER = 'ack'* ]]; then
BUFFER="$BUFFER -l"
fi
BUFFER="vim \$($BUFFER)"
zle accept-line
fi
}
zle -N vim-eval-widget vimify-eval
bindkey '^P' vim-eval-widget
Это работает так: я ищу что-то, используя ack, как ack some-pattern
, Я смотрю на результаты и, если мне это нравится, нажимаю стрелку вверх, чтобы снова получить подтверждение, а затем нажимаю CTRL+P. Затем происходит то, что ZSH добавляет и "-l" для перечисления имен файлов, только если команда начинается с "ack". Затем он помещает "$(...)" вокруг команды и "vim" перед ней. Тогда все это выполняется.
Я использую это довольно часто, чтобы быстро преобразовать время эпохи в полезную метку даты.
perl -l -e 'print scalar(localtime($ARGV[0]))'
Сделайте псевдоним в вашей оболочке:
alias e2d="perl -le \"print scalar(localtime($ARGV[0]));\""
Затем передайте номер эпохи псевдониму.
echo 1219174516 | e2d
Многие программы и утилиты в Unix/Linux используют значения эпох для представления времени, поэтому для меня это оказалось бесценным.
Удалите окончания строк MS-DOS.
perl -p -i -e 's/\r\n$/\n/' htdocs/*.asp
Удалить дубликаты в переменной пути:
set path=(`echo $path | perl -e 'foreach(split(/ /,<>)){print $_," " unless $s{$_}++;}'`)
Извлечение репутации переполнения стека без необходимости открывать веб-страницу:
perl -nle "print ' Stack Overflow ' . $1 . ' (no change)' if /\s{20,99}([0-9,]{3,6})<\/div>/;" "SO.html" >> SOscores.txt
Это предполагает, что страница пользователя уже была загружена в файл SO.html. Я использую wget для этой цели. Обозначение здесь для командной строки Windows; это будет немного отличаться для Linux или Mac OS X. Вывод будет добавлен в текстовый файл.
Я использую его в BAT-скрипте для автоматизации выборки репутации на четырех сайтах в семействе: переполнение стека, сбой сервера, суперпользователь и переполнение мета-стека.
Мне часто нужно видеть читаемую версию PATH во время сценариев оболочки. Следующие однострочники печатают каждую запись пути в отдельной строке.
Со временем этот однострочник прошел несколько этапов:
UNIX (версия 1):
perl -e 'print join("\n",split(":",$ENV{"PATH"}))."\n"'
Windows (версия 2):
perl -e "print join(qq(\n),split(';',$ENV{'PATH'})).qq(\n)"
Оба UNIX/Windows (с использованием q/qq tip из @jf-sebastian) (версия 3):
perl -MFile::Spec -e 'print join(qq(\n),File::Spec->path).qq(\n)' # UNIX
perl -MFile::Spec -e "print join(qq(\n),File::Spec->path).qq(\n)" # Windows
Фильтрует поток разделенных пробелами разделов (списки пар имя / значение), сортируя каждый раздел по отдельности:
perl -00 -ne 'print sort split /^/'
Получите удобочитаемый вывод du
, отсортировано по размеру:
perl -e '%h=map{/.\s/;7x(ord$&&10)+$`,$_}`du -h`;print@h{sort%h}'
Один из последних однострочников, который получил место в моем ~/bin:
perl -ne '$s=time() unless $s; $n=time(); $d=$n-$s; if ($d>=2) { print "$. lines in last $d secs, rate ",$./$d,"\n"; $. =0; $s=$n; }'
Вы будете использовать его против хвоста файла журнала, и он будет печатать скорость строк, которые будут выведены.
Хотите знать, сколько хитов в секунду вы получаете на своих веб-серверах? бревно | this_script.
Сетевые администраторы имеют тенденцию неправильно настраивать "адрес подсети" как "адрес хоста", особенно при использовании автоматического предложения Cisco ASDM. Эта простая однострочная проверка файлов конфигурации на наличие любых таких ошибок конфигурации.
неправильное использование: permit host 10.1.1.0
правильное использование: permit 10.1.1.0 255.255.255.0
perl -ne "print if /host ([\w\-\.]+){3}\.0 /" *.conf
Это было проверено и использовано в Windows, пожалуйста, предложите, если это нужно изменить каким-либо образом для правильного использования.
У меня есть список тегов, с помощью которых я идентифицирую части текста. Основной список имеет формат:
text description {tag_label}
Важно, чтобы {tag_label}
не дублируются. Итак, есть этот хороший простой скрипт:
perl -ne '($c) = $_ =~ /({.*?})/; print $c,"\n" ' $1 | sort | uniq -c | sort -d
Я знаю, что могу сделать все это в shell или perl, но это было первое, что пришло в голову.
Часто мне приходилось преобразовывать табличные данные в файлы конфигурации. Например, поставщики сетевых кабелей предоставляют запись исправлений в формате Excel, и мы должны использовать эту информацию для создания файлов конфигурации. т.е.
Interface, Connect to, Vlan
Gi1/0/1, Desktop, 1286
Gi1/0/2, IP Phone, 1317
должен стать:
interface Gi1/0/1
description Desktop
switchport access vlan 1286
и так далее. Одна и та же задача вновь появляется в нескольких формах в различных задачах администрирования, где к табличным данным необходимо добавить их имя поля и перенести в плоскую структуру. Я много раз видел, как некоторые администраторы БД готовили свои операторы SQL из таблицы Excel. Это может быть достигнуто с помощью этого простого однострочного. Просто сохраните табличные данные в формате CSV, используя ваш любимый инструмент для работы с электронными таблицами, и запустите эту однострочную строку. Имена полей в строке заголовка добавляются к отдельным значениям ячеек, поэтому вам, возможно, придется изменить их в соответствии с вашими требованиями.
perl -F, -lane "if ($.==1) {@keys = @F} else{print @keys[$_].$F[$_] foreach(0..$#F)} "
Предостережение заключается в том, что ни одно из имен или значений полей не должно содержать запятых. Возможно, это может быть доработано, чтобы перехватывать такие исключения в одну строку, пожалуйста, улучшите это, если это возможно.
Разверните все вкладки до пробелов: perl -pe'1while+s/\t/" "x(8-pos()%8)/e'
Конечно, это можно сделать с помощью:set et,:ret в Vim.
Вот тот, который мне удобен при работе со сжатыми файлами журналов коллекции:
open STATFILE, "zcat $logFile|" or die "Can't open zcat of $logFile" ;
В какой-то момент я обнаружил, что все, что я хотел бы сделать с perl, достаточно коротким, чтобы его можно было выполнить в командной строке с помощью 'perl -e', можно было сделать лучше, проще и быстрее с обычными функциями ZSH без лишних кавычек. Например, приведенный выше пример можно сделать так:
for foo in *.avi; mv *.srt ${foo:r}.srt
ОБНОВИТЬ
Онлайнер выше ошибочно, извините, что не читал внимательно. Вот правильная версия:
srt=(*.srt); for foo in *.avi; mv $srt[1] ${foo:r}.srt && srt=($srt[2,-1])