您最新的有用 Perl 单线(或涉及 Perl 的管道)是啥? [关闭]
Posted
技术标签:
【中文标题】您最新的有用 Perl 单线(或涉及 Perl 的管道)是啥? [关闭]【英文标题】:What is your latest useful Perl one-liner (or a pipe involving Perl)? [closed]您最新的有用 Perl 单线(或涉及 Perl 的管道)是什么? [关闭] 【发布时间】:2010-09-11 19:55:35 【问题描述】:单行应该:
解决实际问题 不要过于神秘(应该易于理解和重现) 值得花时间写它(不应该太聪明)我正在寻找实用的提示和技巧(perldoc perlrun
的补充示例)。
【问题讨论】:
"last" 的意思是“final”,因为我再也不会写另一个了。那不会发生的。只要提示接受,我将继续编写 Perl 单行代码。也许你的意思是“最新的”。 谢谢。我已经更正了标题。我最后一顿饭的时间还没到:) 【参考方案1】:请看我的幻灯片"A Field Guide To The Perl Command Line Options."
【讨论】:
`-e 示例幻灯片:在 Windows 上,我更喜欢 q() 和 qq() 而不是引号。它允许我通过仅替换两个外部引号来使用相同的 Linux 单行代码。 Windows:perl -E"say q(Hello, World)"。 Linux:perl -E'say q(Hello, World)' 死链接... 镜像链接:web.archive.org/web/20120916013412/http://petdance.com/perl/…【参考方案2】:鱿鱼日志文件。他们很棒,不是吗?除了默认情况下,它们将秒数作为时间字段。这是一个从 squid 日志文件中读取并将时间转换为人类可读日期的单行程序:
perl -pe's/([\d.]+)/localtime $1/e;' access.log
只需稍加调整,您就可以使其仅显示带有您感兴趣的关键字的行。***.com 的以下监视仅访问和打印这些行,并带有人类可读的日期。为了让它更有用,我给它输出tail -f
,这样我就可以实时看到访问:
tail -f access.log | perl -ne's/([\d.]+)/localtime $1/e,print if /***\.com/'
【讨论】:
【参考方案3】:问题:媒体播放器不会自动加载字幕,因为它们的名称与对应的视频文件不同。
解决方案:重命名所有 *.srt(带字幕的文件)以匹配 *.avi(带视频的文件)。
perl -e'while(<*.avi>) s/avi$/srt/; rename <*.srt>, $_ '
CAVEAT:原始视频和字幕文件名的排序顺序应相同。
这里是上述单行代码的更详细版本:
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'
【讨论】:
【参考方案4】:你可能不认为这是 Perl,但我虔诚地使用 ack(这是一个用 Perl 编写的智能 grep 替代品),这让我可以编辑,例如,我所有的 Perl 测试,这些测试访问我们的特定部分接口:
vim $(ack --perl -l 'api/v1/episode' t)
附带说明,如果你使用 vim,你可以run all of the tests in your editor's buffers。
对于更明显(如果简单的话)Perl 的东西,我需要知道有多少测试程序用尽了 t/lib/TestPM 目录中的测试装置(为了清楚起见,我已经删减了命令)。
ack $(ls t/lib/TestPM/|awk -F'.' 'print $1'|xargs perl -e 'print join "|" => @ARGV') aggtests/ t -l
请注意“加入”如何将结果转换为正则表达式以供 ack 使用。
【讨论】:
【参考方案5】:使用find ... -exec rm \;
删除目录树中某处的一组文件的常见习惯用法并不是特别有效,因为它为找到的每个文件执行一次rm
命令。我的一个习惯是在计算机还没有那么快(dagnabbit!)的时候出生的,就是用一个 perl 调用代替对 rm
的多次调用:
find . -name '*.whatever' | perl -lne unlink
命令行的perl
部分读取find
发出的文件列表*,每行一个,修剪换行符,并使用perl 的内置unlink()
函数删除文件,该函数采用@如果没有提供显式参数,则 987654329@ 作为其参数。 (由于-n
标志,$_
被设置为每一行输入。)(*现在,大多数find
命令默认使用-print
,所以我可以省略这部分。)
我喜欢这个习语,不仅因为它的效率(现在可能不那么重要了),还因为它比输入传统的-exec rm \;
序列具有更少的和弦/尴尬键。它还避免了由带有空格、引号等的文件名引起的引用问题,其中我有很多。 (更强大的版本可能使用find
的-print0
选项,然后要求perl
读取以空值分隔的记录而不是行,但我通常非常确信我的文件名不包含嵌入的换行符。)
【讨论】:
在 Perl 还没有出现在 Larry 眼中之前,我一直在使用 xargs 来解决这个问题 :-)。 公平,因为它是 Perl 相关的主题;但另一个更强大的 POSIX 版本可以使用find ... -print0 | xargs -0 -- rm
或使用 GNU findutils:find ... --exec rm +
⁽ʳᵉᶠ⁾【参考方案6】:
所有答案都集中在一个地方:
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", subreverse @_[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)'
查看相应答案的描述。
【讨论】:
【参考方案7】:我用得最多的 Perl 单行是 Perl 计算器
perl -ple '$_=eval'
【讨论】:
如果您运行的是 Perl 5.10,您可以运行 perl -plE '$_=eval' 来启用 5.10 的功能。【参考方案8】:$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",
subreverse @_[0..2]->(localtime $F[0]), @F[2,4]'
Perl 管道所做的是首先将 autoflush 设置为 true,以便立即打印出任何被执行的操作。否则,它会将输出分块,并且当输出缓冲区填满时会收到一批行。 -a 开关在空白处拆分每个输入行,并将结果保存在数组 @F 中(该功能的灵感来自 awk 将输入记录拆分为其 $1、$2、$3... 变量的能力)。
它检查该行中的第 7 个字段是否包含我们寻找的 URI(使用 \Q 来避免我们逃避无趣的元字符的痛苦)。如果找到匹配项,它会漂亮地打印时间、源 IP 和从远程站点返回的字节数。
时间是通过在第一个字段中获取纪元时间并使用“本地时间”将其分解为其组成部分(小时、分钟、秒、日、月、年)来获得的。它取前三个元素的切片返回,秒、分和小时,并颠倒顺序得到小时、分钟和秒。这作为一个三元素数组返回,以及来自原始 @F 数组的第三个(IP 地址)和第五个(大小)的切片。这五个参数被传递给 sprintf 来格式化结果。
【讨论】:
【参考方案9】:@dr_pepper
删除$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;
【讨论】:
感谢您向我展示这一点,我正在寻找更短的单线来执行此操作。在我的环境中,使用小写 $path 时,空格是分隔符。使用大写 $PATH 更好吗? 在我的 shell (bash) 中,$path 和 $PATH 是不同的变量(名称区分大小写:$ a=2; A=3; echo $(($a * $A)) 这个打印“6”。 可以使用tr
、sort
、uniq
、cut
和管道程序的组合来删除重复项。
但是,使用 tr、sort 等会改变路径顺序,可能会导致不良副作用。
在ZSH中变量path
绑定变量PATH
,所以PATH
总是path
的元素,用冒号连接,path
总是包含PATH
的块按列拆分。要使它们唯一,只需将 -U 修饰符应用于变量之一: typeset -U PATH【参考方案10】:
我经常使用它来快速将纪元时间转换为有用的日期戳。
perl -l -e 'print scalar(localtime($ARGV[0]))'
在你的 shell 中创建一个别名:
alias e2d="perl -le \"print scalar(localtime($ARGV[0]));\""
然后将一个纪元编号传递给别名。
echo 1219174516 | e2d
Unix/Linux 上的许多程序和实用程序使用 epoch 值来表示时间,因此这对我来说非常宝贵。
【讨论】:
要从纪元秒中获取可读的日期戳,您还可以使用带有@符号的 GNU 日期:date --date=@1219174516【参考方案11】:删除路径变量中的重复项:
set path=(`echo $path | perl -e 'foreach(split(/ /,<>))print $_," " unless $s$_++;'`)
【讨论】:
$path 中路径之间的分隔符是什么?看我的回答。【参考方案12】:删除 MS-DOS 行尾。
perl -p -i -e 's/\r\n$/\n/' htdocs/*.asp
【讨论】:
1.-i
需要后缀,例如 -i.bak
。 2. 无法在 Windows 上运行。
我想知道如何在 Windows 中做 Perl 派。感谢您的提示。【参考方案13】:
无需打开网页即可提取 Stack Overflow 信誉:
perl -nle "print ' Stack Overflow ' . $1 . ' (no change)' if /\s20,99([0-9,]3,6)<\/div>/;" "SO.html" >> SOscores.txt
这假设用户页面已经下载到文件 SO.html。我为此目的使用 wget。这里的符号是针对 Windows 命令行的;对于 Linux 或 Mac OS X 会略有不同。输出附加到文本文件中。
我在 BAT 脚本中使用它来自动对家族中四个站点的声誉进行抽样: 堆栈溢出、服务器故障、超级用户和元堆栈溢出。
【讨论】:
【参考方案14】:回复Ovid's Vim/ack combination:
我也经常搜索一些东西,然后想在 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”放在它前面。然后整个事情都被执行了。
【讨论】:
【参考方案15】:在编写 shell 脚本时,我经常需要查看 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(使用来自@j-f-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 -MFile::Spec -E '$,=qq(\n); say File::Spec->path'
perl -MFile::Spec::Functions -E '$,=qq(\n); say path'
@J.F. Sebastian:当使用 -E 运行时,我在 Windows 和 UNIX 上都得到Unrecognized switch: -E (-h will show valid options).
。我在两个平台上都运行 perl v5.8.8。
-E
启用可选功能,例如 say()
和 5.10
(自 2007 年起)。
@J.F. Sebastian:谢谢,这是一个有用的花絮!【参考方案16】:
在我的 ~/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 日志 | this_script。
【讨论】:
它是一个迷你管道查看器 (pv
) ivarch.com/programs/pv.shtml>。但是你的单行代码可以在 Windows 上运行。【参考方案17】:
从du
获取人类可读的输出,按大小排序:
perl -e '%h=map/.\s/;7x(ord$&&10)+$`,$_`du -h`;print@hsort%h'
【讨论】:
【参考方案18】:过滤以空格分隔的节流(名称/值对列表), 分别对每个节进行排序:
perl -00 -ne 'print sort split /^/'
【讨论】:
sort()
将在段落顶部放置空行。我猜你实际上是这个意思: perl -00 -ne'($n, @a) = sort split /^/; print @a, $n' 如果最后一段之后没有换行符,则两个单行都会失败。【参考方案19】:
网络管理员倾向于将“子网地址”错误配置为“主机地址”,尤其是在使用 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 上测试和使用的,请建议是否应该以任何方式对其进行修改以正确使用。
【讨论】:
【参考方案20】:将所有制表符扩展到空格:perl -pe'1while+s/\t/" "x(8-pos()%8)/e'
当然,这可以通过 Vim 中的 :set et, :ret 来完成。
【讨论】:
perl -pe'1while+s/\t/" "x(8-(pos)%8)/e' 需要括号。【参考方案21】:我有一个标签列表,我用它来识别部分文本。主列表的格式为:
text description tag_label
tag_label
不能重复,这一点很重要。所以有这个不错的简单脚本:
perl -ne '($c) = $_ =~ /(.*?)/; print $c,"\n" ' $1 | sort | uniq -c | sort -d
我知道我可以在 shell 或 perl 中完成所有工作,但这是我首先想到的。
【讨论】:
perl -ne'$f$1++ while /(.*?)/g; END print "$f$_ $_\n" for (sort $f$a <=> $f$b keys %f) ' $1
。你是对的,对于这样的任务,首先要考虑的事情就足够了。顺便说一句,你确定每行只能有一个标签吗?【参考方案22】:
我经常不得不将表格数据转换为配置文件。例如,网络布线供应商以 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
等等。相同的任务在各种管理任务中以多种形式重新出现,其中需要在表格数据前面加上其字段名称并转换为平面结构。我已经看到一些 DBA 浪费了很多时间从 excel 表中准备他们的 SQL 语句。可以使用这个简单的单线来实现。只需使用您最喜欢的电子表格工具将表格数据保存为 CSV 格式并运行此单行。标题行中的字段名称会添加到各个单元格值的前面,因此您可能需要对其进行编辑以符合您的要求。
perl -F, -lane "if ($.==1) @keys = @F elseprint @keys[$_].$F[$_] foreach(0..$#F) "
需要注意的是,任何字段名称或值都不应包含任何逗号。也许这可以进一步阐述以在一行中捕获此类异常,如果可能,请改进这一点。
【讨论】:
【参考方案23】:在处理集合压缩日志文件时,我觉得很方便:
open STATFILE, "zcat $logFile|" or die "Can't open zcat of $logFile" ;
【讨论】:
单行是指一行中的整个程序,而不是程序中有用的一行。 我自己实际上会把它分成两行或更多行。【参考方案24】:有时我发现我想用 Perl 做的任何事情只要用“perl -e”在命令行上完成就可以用普通的 Z shell 功能做得更好、更容易、更快捷,而没有麻烦的引用。例如。上面的例子可以这样完成:
srt=(*.srt); for foo in *.avi; mv $srt[1] $foo:r.srt && srt=($srt[2,-1])
【讨论】:
glob-in-scalar-context 真的很容易出错;应尽可能避免。 新版本不会因为 mv 故障而失去同步吗? 嗯,这个东西的整个想法有点不稳定,因为它假设每个 .avi 都有一个 .srt,并且当按字母顺序排序时,每个 avi/srt 对都在相同的位置在列表中。但是,您可以将 && 替换为 ;并在它周围放上大括号。 ;)以上是关于您最新的有用 Perl 单线(或涉及 Perl 的管道)是啥? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章