单线应该:
- 解决一个现实世界的问题
- 不要过于神秘(应该易于理解和重现)
- 值得花时间写它(不应该太聪明)
我正在寻找实用的技巧和窍门(补充示例perldoc perlrun
)。
单线应该:
我正在寻找实用的技巧和窍门(补充示例perldoc perlrun
)。
请参阅我的幻灯片以获取“Perl 命令行选项的现场指南”。
鱿鱼日志文件。他们很棒,不是吗?除了默认情况下,它们具有从时代开始的秒数作为时间字段。这是一个从 squid 日志文件中读取并将时间转换为人类可读日期的单行程序:
perl -pe's/([\d.]+)/localtime $1/e;' access.log
稍作调整,您就可以让它只显示带有您感兴趣的关键字的行。stackoverflow.com 的以下手表仅访问和打印这些行,并带有人类可读的日期。为了使它更有用,我给它的输出tail -f
,所以我可以看到实时访问:
tail -f access.log | perl -ne's/([\d.]+)/localtime $1/e,print if /stackoverflow\.com/'
问题:媒体播放器不会自动加载字幕,因为它们的名称与相应的视频文件不同。
解决方案:重命名所有 *.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,但我虔诚地使用ack(它是一个用 Perl 编写的智能 grep 替代品),这让我可以编辑,例如,我所有的访问我们 API 特定部分的 Perl 测试:
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
请注意“加入”如何将结果转换为正则表达式以供 ack 使用。
find ... -exec rm {} \;
用于删除目录树中某处的一组文件的常见习惯用法并不是特别有效,因为它rm
为找到的每个文件执行一次命令。我的一个习惯是在计算机还没有那么快(dagnabbit!)的时代出生的,就是rm
用一个对 perl 的调用来代替许多调用:
find . -name '*.whatever' | perl -lne unlink
命令行的perl
一部分读取由 发出的文件列表find
,每行一个,修剪换行符,并使用 perl 的内置unlink()
函数删除文件,$_
如果没有提供显式参数,则该函数将其作为参数。($_
由于该-n
标志,设置为每一行输入。)(*这些天,大多数find
命令-print
默认情况下,所以我可以省略这部分。)
我喜欢这个习语,不仅因为它的效率(现在可能不那么重要了),而且因为它比输入传统-exec rm {} \;
序列的和弦/尴尬键更少。它还避免了由带有空格、引号等的文件名引起的引用问题,其中我有很多。(更强大的版本可能会使用find
'-print0
选项,然后要求perl
读取以空值分隔的记录而不是行,但我通常非常确信我的文件名不包含嵌入的换行符。)
来自一个地方收集的答案的所有单行:
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 one-liner 是 Perl 计算器
perl -ple '$_=eval'
$work 最大的带宽消耗之一是下载网络广告,所以我正在寻找等待采摘的低垂果实。我已经摆脱了谷歌广告,现在我的视线中有微软。所以我在日志文件上运行了一个tail,并挑选出感兴趣的行:
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 和从远程站点返回的字节数。
时间是通过在第一个字段中获取纪元时间并使用“本地时间”将其分解为其组成部分(小时、分钟、秒、日、月、年)来获得的。它取前三个元素的切片返回,秒、分和小时,并颠倒顺序得到小时、分钟和秒。这作为一个三元素数组返回,以及来自原始 @F 数组的第三个(IP 地址)和第五个(大小)的切片。这五个参数被传递给 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 中打开匹配的文件,所以前段时间我给自己做了一个小快捷方式(我想只在Z shell中工作):
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
. 我查看结果,如果我喜欢它,我按向上箭头再次获取 ack-line,然后按Ctrl+ P。然后发生的情况是,仅当命令以“ack”开头时,Z shell 才会附加和“-l”以列出文件名。然后它把“$(...)”放在命令周围,把“vim”放在它前面。然后整个事情被执行。
我经常使用它来快速将纪元时间转换为有用的日期戳。
perl -l -e 'print scalar(localtime($ARGV[0]))'
在你的 shell 中创建一个别名:
alias e2d="perl -le \"print scalar(localtime($ARGV[0]));\""
然后将一个纪元号通过管道传递给别名。
echo 1219174516 | e2d
Unix/Linux 上的许多程序和实用程序使用 epoch 值来表示时间,所以这对我来说是无价的。
删除路径变量中的重复项:
set path=(`echo $path | perl -e 'foreach(split(/ /,<>)){print $_," " unless $s{$_}++;}'`)
删除 MS-DOS 行尾。
perl -p -i -e 's/\r\n$/\n/' htdocs/*.asp
无需打开网页即可提取 Stack Overflow 信誉:
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 脚本中使用它来自动对家族中四个站点的声誉进行抽样:Stack Overflow、Server Fault、Super User 和 Meta Stack Overflow。
在编写 shell 脚本时,我经常需要查看 PATH 的可读版本。以下单行将每个路径条目打印在其自己的行上。
随着时间的推移,这种单线已经经历了几个阶段:
Unix(版本 1):
perl -e 'print join("\n",split(":",$ENV{"PATH"}))."\n"'
视窗(版本 2):
perl -e "print join(qq(\n),split(';',$ENV{'PATH'})).qq(\n)"
Unix/Windows(使用来自@jf-sebastian 的 q/qq 提示)(版本 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 /^/'
在我的 ~/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; }'
您可以将它用于日志文件的尾部,它会打印输出行的速率。
想知道您的网络服务器每秒获得多少点击?尾 -f 日志 | 这个脚本。
从 获取人类可读的输出du
,按大小排序:
perl -e '%h=map{/.\s/;7x(ord$&&10)+$`,$_}`du -h`;print@h{sort%h}'
网络管理员倾向于将“子网地址”错误配置为“主机地址”,尤其是在使用 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 上测试和使用的,请建议是否应该以任何方式对其进行修改以正确使用。
将所有制表符展开为空格:perl -pe'1while+s/\t/" "x(8-pos()%8)/e'
当然,这可以通过 Vim 中的 :set et, :ret 来完成。
我有一个标签列表,我用它来识别部分文本。主列表的格式为:
text description {tag_label}
重要的{tag_label}
是不要重复。所以有这个很好的简单脚本:
perl -ne '($c) = $_ =~ /({.*?})/; print $c,"\n" ' $1 | sort | uniq -c | sort -d
我知道我可以在 shell 或 perl 中做很多事情,但这是我想到的第一件事。
我经常不得不将表格数据转换为配置文件。例如,网络布线供应商提供 Excel 格式的修补记录,我们必须使用该信息来创建配置文件。IE,
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
等等。相同的任务在各种管理任务中以多种形式重新出现,其中需要在表格数据前面加上其字段名称并转换为平面结构。我已经看到一些 DBA 浪费了很多时间从 excel 表中准备他们的 SQL 语句。可以使用这个简单的单线来实现。只需使用您喜欢的电子表格工具将表格数据保存为 CSV 格式并运行此单行。标题行中的字段名称会添加到各个单元格值的前面,因此您可能必须对其进行编辑以符合您的要求。
perl -F, -lane "if ($.==1) {@keys = @F} else{print @keys[$_].$F[$_] foreach(0..$#F)} "
需要注意的是,任何字段名称或值都不应包含任何逗号。也许这可以进一步阐述以在一行中捕获此类异常,如果可能,请改进这一点。
这是我在处理集合压缩日志文件时发现的一个方便的方法:
open STATFILE, "zcat $logFile|" or die "Can't open zcat of $logFile" ;
有时我发现我想用 Perl 做的任何事情只要足够短,可以在命令行上用 'perl -e' 完成,可以用普通的 Z shell 功能更好、更容易、更快地完成,而无需麻烦的引用。例如上面的例子可以这样完成:
srt=(*.srt); for foo in *.avi; mv $srt[1] ${foo:r}.srt && srt=($srt[2,-1])