[英]How do you use sed from Perl?
我知道如何將sed
與grep
一起使用,但在 Perl 內,以下失敗。 如何讓sed
在 Perl 程序中工作?
chomp (my @lineNumbers=`grep -n "textToFind" $fileToProcess | sed -n 's/^\([0-9]*\)[:].*/\1/p'`)
建議:使用 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, $_;
}
我很驚訝沒有人提到s2p實用程序,它將 sed“腳本”(你知道,大多數時候是 oneliners)轉換為有效的 perl。 (還有一個用於 awk 的 a2p 實用程序......)
據說 Larry Wall 寫了 Perl,因為他發現了 sed 和 awk 不可能做的事情。 其他答案有這個權利,請改用 Perl 正則表達式。 您的代碼將具有更少的外部依賴,更多人可以理解(Perl 的用戶群比 sed 用戶群大得多),並且您的代碼將是跨平台的,無需額外工作。
編輯:Paul Tomblin 在他對我的回答的評論中講述了一個精彩的故事。 我把它放在這里是為了增加它的知名度。
“Henry Spencer 用 Awk 做了一些了不起的事情,他聲稱在向 Larry Wall 演示了一些 awk 的東西后,Larry 說如果他知道他就不會打擾 Z0114AD06D728F1834E36FE14。” ——保羅·湯布林
使用權力盧克:
$ 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;
或與原始解決方案相同的 memory 罪魁禍首
my @linenumbers = do {
open my $fh, '<', $fileToProcess or die "Can't open $fileToProcess: $!";
my $i;
map { ( ++$i ) x /textToFind/ } <$fh>
};
您可以使用
perl -pe 's/search/replace/g'
代替
sed 's/search/replace/'
.. 然而..
這些適用於命令行或 shell 腳本。 由於您已經在 perl 腳本中,因此上面的“Paul Tomblin”給出了正確答案。
玩得開心,eKerner.com
如果您有一個大的sed
表達式,您可以使用s2p
將其轉換為perl
程序。
如果你運行 < s2p 's/^\([0-9]*\)[:].*/\1/p'
>,你會得到:
#!/opt/perl/bin/perl -w
eval 'exec /opt/perl/bin/perl -S $0 ${1+"$@"}'
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 替代 Sed 的方法:
代替:
sed "s/xxx/yyy/g" files_to_process
利用:
perl -i.bak -pe "s/xxx/yyy/g" files_to_process
這將就地修改文件並對每個修改的文件進行備份 ( .bak
)。
使用 Perl 比使用 grep 和 sed 更容易; 看到另一個答案。
您的代碼失敗,因為 Perl 與 sed 代碼中的反斜杠混淆。 為防止這種情況,請將您的 sed 代碼寫入'a single-quoted Perl string'
,然后使用\Q$sedCode\E
將代碼插入 Z2591C98B70119FE624898B1E424 命令中。 (關於\Q...E
,請參閱perldoc -f quotemeta 。它通常的目的是為正則表達式引用字符,但它也適用於 shell 命令。)
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
output 是1, 3
。
編輯:好的,我現在修好了。
use File::Grep qw/fmap/;
my @lineNumbers = fmap { /$pattern/ ? $_[1] : () } $fileToProcess;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.