我知道如何将sed
与grep
一起使用,但在Perl中以下失败。如何获得sed
在Perl程序中工作?你如何从Perl使用sed?
chomp (my @lineNumbers=`grep -n "textToFind" $fileToProcess | sed -n 's/^\([0-9]*\)[:].*/\1/p'`)
我知道如何将sed
与grep
一起使用,但在Perl中以下失败。如何获得sed
在Perl程序中工作?你如何从Perl使用sed?
chomp (my @lineNumbers=`grep -n "textToFind" $fileToProcess | sed -n 's/^\([0-9]*\)[:].*/\1/p'`)
我很惊讶,没有人提到s2p实用程序,它将sed“脚本”(你知道,大部分时间在oneliners上)转换为有效的perl。 (还有一个awk的a2p工具...)
建议:使用Perl正则表达式和替换代替grep或sed。
它几乎是相同的语法,但功能更强大。另外最后它会比调用额外的sed过程更有效率。
任何你需要用grep或sed做的事情都可以在perl中更简单地完成。例如(这大致是正确的,但可能是错误的):
my @linenumbers;
open FH "<$fileToProcess";
while (<FH>)
{
next if (!m/textToFind/);
chomp;
s/^\([0-9]*\)[:].*/\1/;
push @lineNumbers, $_;
}
编辑:好的,我现在修好了。
use File::Grep qw/fmap/;
my @lineNumbers = fmap { /$pattern/ ? $_[1] :() } $fileToProcess;
假设Larry Wall写了Perl,因为他发现了一些无法用sed和awk做的事情。其他答案有这个权利,而是使用Perl正则表达式。你的代码将会减少外部依赖,对于更多人来说是可以理解的(Perl的用户基础比sed用户基础大得多),你的代码将是跨平台的,没有额外的工作。
编辑:Paul Tomblin在他评论我的答案时提到了一个很好的故事。我把它放在这里来增加它的重要性。
“与Awk做了一些令人惊奇的事情的Henry Spencer声称,在向Larry Wall演示了一些awk的东西之后,Larry说如果他知道的话他不会为Perl烦恼。保罗 - 汤布林
使用电源卢克:
$ echo -e "a\nb\na"|perl -lne'/a/&&print$.'
1
3
因此,当你想同认为,因为这缓慢和过于复杂的grep
和sed
组合,你可以做到这一点更简单,更快的在Perl本身:
my @linenumbers;
open my $fh, '<', $fileToProcess or die "Can't open $fileToProcess: $!";
while (<$fh>)
{
/textToFind/ and push @lineNumbers, $.;
}
close $fh;
或具有与原始解决方案相同的内存元凶
my @linenumbers = do {
open my $fh, '<', $fileToProcess or die "Can't open $fileToProcess: $!";
my $i;
map { (++$i) x /textToFind/ } <$fh>
};
如果您有大的sed
表达式,则可以使用s2p
将其转换为perl
程序。
如果运行 < s2p 's/^\([0-9]*\)[:].*/\1/p'
>,这是你会得到什么:
#!/opt/perl/bin/perl -w
eval 'exec /opt/perl/bin/perl -S $0 ${1+"[email protected]"}'
if 0;
$0 =~ s/^.*?(\w+)[\.\w+]*$/$1/;
use strict;
use Symbol;
use vars qw{ $isEOF $Hold %wFiles @Q $CondReg
$doAutoPrint $doOpenWrite $doPrint };
$doAutoPrint = 1;
$doOpenWrite = 1;
# prototypes
sub openARGV();
sub getsARGV(;\$);
sub eofARGV();
sub printQ();
# Run: the sed loop reading input and applying the script
#
sub Run(){
my($h, $icnt, $s, $n);
# hack (not unbreakable :-/) to avoid // matching an empty string
my $z = "\000"; $z =~ /$z/;
# Initialize.
openARGV();
$Hold = '';
$CondReg = 0;
$doPrint = $doAutoPrint;
CYCLE:
while(getsARGV()){
chomp();
$CondReg = 0; # cleared on t
BOS:;
# s/^\([0-9]*\)[:].*/\1/p
{ $s = s /^(\d*)[:].*/${1}/s;
$CondReg ||= $s;
print $_, "\n" if $s;
}
EOS: if($doPrint){
print $_, "\n";
} else {
$doPrint = $doAutoPrint;
}
printQ() if @Q;
}
exit(0);
}
Run();
# openARGV: open 1st input file
#
sub openARGV(){
unshift(@ARGV, '-') unless @ARGV;
my $file = shift(@ARGV);
open(ARG, "<$file")
|| die("$0: can't open $file for reading ($!)\n");
$isEOF = 0;
}
# getsARGV: Read another input line into argument (default: $_).
# Move on to next input file, and reset EOF flag $isEOF.
sub getsARGV(;\$){
my $argref = @_ ? shift() : \$_;
while($isEOF || ! defined($$argref = <ARG>)){
close(ARG);
return 0 unless @ARGV;
my $file = shift(@ARGV);
open(ARG, "<$file")
|| die("$0: can't open $file for reading ($!)\n");
$isEOF = 0;
}
1;
}
# eofARGV: end-of-file test
#
sub eofARGV(){
return @ARGV == 0 && ($isEOF = eof(ARG));
}
# makeHandle: Generates another file handle for some file (given by its path)
# to be written due to a w command or an s command's w flag.
sub makeHandle($){
my($path) = @_;
my $handle;
if(! exists($wFiles{$path}) || $wFiles{$path} eq ''){
$handle = $wFiles{$path} = gensym();
if($doOpenWrite){
if(! open($handle, ">$path")){
die("$0: can't open $path for writing: ($!)\n");
}
}
} else {
$handle = $wFiles{$path};
}
return $handle;
}
# printQ: Print queued output which is either a string or a reference
# to a pathname.
sub printQ(){
for my $q (@Q){
if(ref($q)){
# flush open w files so that reading this file gets it all
if(exists($wFiles{$$q}) && $wFiles{$$q} ne ''){
open($wFiles{$$q}, ">>$$q");
}
# copy file to stdout: slow, but safe
if(open(RF, "<$$q")){
while(defined(my $line = <RF>)){
print $line;
}
close(RF);
}
} else {
print $q;
}
}
undef(@Q);
}
不完全是值得做的小表情。
可以代替使用
perl -pe 's/search/replace/g'
的
sed 's/search/replace/'
..但是..
这些都是为了命令行或shell脚本。 既然你已经在Perl脚本中, 上面的“Paul Tomblin”给出了正确的答案。
玩得开心, eKerner.com
这里是你如何使用Perl作为桑达更换:
相反的:
sed "s/xxx/yyy/g" files_to_process
用途:
perl -i.bak -pe "s/xxx/yyy/g" files_to_process
这将在原地修改文件并对每个修改后的文件进行备份(.bak
)。
使用Perl比使用grep和sed更容易;见another answer。
你的代码失败了,因为Perl与sed代码中的反斜杠混淆了。为防止出现这种情况,请在'a single-quoted Perl string'
中编写您的sed代码,然后使用\Q$sedCode\E
将代码内插到shell命令中。 (关于\Q...E
,见perldoc -f quotemeta,其通常的目的是引号字符的正则表达式,但it also works with shell commands)
my $fileToProcess = "example.txt";
my $sedCode = 's/^\([0-9]*\)[:].*/\1/p';
chomp(my @linenumbers =
`grep -n "textToFind" \Q$fileToProcess\E | sed -n \Q$sedCode\E`);
printf "%s\n", join(', ', @linenumbers);
鉴于example.txt
与
this has textToFind
this doesn't
textToFind again
textNotToFind
输出为1, 3
。
grep也是不必要的 – dsm 2009-03-04 16:36:31
谢谢@dsm!我更新了答案 – 2009-03-04 16:37:09
同意,最好不要在语言的可用API之外进行不必要的绕行。 – 2009-03-04 16:45:12