2017-07-22 74 views
0

我正在嘗試創建一個Perl腳本,用於過濾STDIN上顯示的數據,將所有出現的 一個字符串更改爲另一個字符串,並輸出所有輸入行,並將其更改並保留爲STDOUT。 FROMSTRING和TOSTRING可以是PERL兼容的正則表達式。我無法獲得匹配的輸出。帶有替換的Perl過濾器

這裏是什麼,我想實現的例子。

echo "Today is Saturday" | f.pl 'a' '@' 

輸出[email protected] is [email protected]@y

echo io | filter.pl '([aeiou])([aeiou])' '$2$1' 

輸出oi。發現

#!/usr/bin/perl 
use strict; 
use warnings; 
if (@ARGV != 2){ 
     print STDERR "Usage: ./filter.pl FROMSTRING TOSTRING\n" 
} 
exit 1; 
my $FROM = $ARGV[0]; 
my $TO = $ARGV[1]; 
my $inLine = ""; 
while (<STDIN>){ 
$inLine = $_; 
$inLine =~ s/$FROM/$TO/; 
print $inLine 
} 
exit 0; 
+0

注 - 可以做'while(my $ inline = ){...}'。另外,'我的($ from,$ to)= @ARGV;' – zdim

+0

或'print s/$ from/$ to/r ;''用'/ r'修飾符[來自5.14](http:// perldoc。 perl.org/perl5140delta.html) – zdim

回答

0

三個錯誤:

; after error message 
exit 1; 
$inLine =~ s/$FROM/$TO/g; 

像:

#!/usr/bin/perl 
use strict; 
use warnings; 
if (@ARGV != 2){ 
     print STDERR "Usage: ./filter.pl FROMSTRING TOSTRING\n"; 
     exit 1; 
} 
my $FROM = $ARGV[0]; 
my $TO = $ARGV[1]; 
my $inLine = ""; 
while (<STDIN>){ 
$inLine = $_; 
$inLine =~ s/$FROM/$TO/g; 
print $inLine 
} 
exit 0; 
+0

謝謝,那些只是我忽略的簡單的語法錯誤。但是,當我嘗試更復雜的東西,如filter.pl'([aeiou])([aeiou])''$ 2 $ 1''它沒有正確完成。 – user8351474

+0

在塊中的最後一條語句之後不需要';'。 – melpomene

+0

@ user8351474「$ 2 $ 1」是一個文字字符串(只是這四個字符),這就是它在正則表達式中的處理方式。所以你會得到'$ 2 $ 1'的結果。所以你需要賦予輸入字符串在正則表達式中的含義(在這種情況下,變量'$ 2'和'$ 1'),這並不簡單。查看[這篇文章](https://stackoverflow.com/a/45233407/4653379)爲同一種問題。 – zdim

6

首先,一個操作s/.../.../的替換部分不是正則表達式;它像雙引號字符串一樣工作。

有幾個與你的代碼的問題。

  • exit 1;語句出現在主代碼的中間,而不是在錯誤塊。你可能想:

    if (@ARGV != 2) { 
        print STDERR "Usage: ./filter.pl FROMSTRING TOSTRING\n"; 
        exit 1; 
    } 
    
  • 你錯過,如果你想多換人在同一行發生一g標誌:

    $inLine =~ s/$FROM/$TO/g; 
    
  • 有沒有必要預先聲明$inLine;它只在一個塊中使用。

  • 但也沒有必要讀行成$_只是將其複製到$inLine
  • 它通常使用$names_like_this變量和函數,而不是$namesLikeThis
  • 可以使用$0,而不是在錯誤信息硬編碼的程序名。
  • exit 0;是在端冗餘。

以下是更接近我怎麼會寫:

#!/usr/bin/perl 
use strict; 
use warnings; 

if (@ARGV != 2) { 
    die "Usage: $0 FROMSTRING TOSTRING\n"; 
} 

my ($from, $to) = @ARGV; 

while (my $line = readline STDIN) { 
    $line =~ s/$from/$to/g; 
    print $line; 
} 

這就是說,這些都不滿足您與'$2$1'作爲替換第二個例子。上面的代碼不會做你想要的,因爲$to是一個純字符串。 Perl不會掃描它來尋找類似$1的東西並替換它們。

當您在代碼中編寫"foo $bar baz"時,它與'foo ' . $bar . ' baz'意思相同,但這僅適用於代碼,即東西,字面上出現在您的源代碼。在運行時不會重新掃描$bar的內容以擴展例如\n$quux。這也適用於$1和朋友,這只是普通變量。

那麼你如何得到'$2$1'工作?

一種方法是混淆eval,但我不喜歡它,因爲它是eval:如果你不是很小心,它會允許某人通過傳遞正確的替換字符串來執行任意代碼「string 」。

不用eval就可以做到這一點,甚至可以使用例如Data::Munge::replace

#!/usr/bin/perl 
use strict; 
use warnings; 
use Data::Munge qw(replace); 

if (@ARGV != 2) { 
    die "Usage: $0 FROMSTRING TOSTRING\n"; 
} 

my ($from, $to) = @ARGV; 

while (my $line = readline STDIN) { 
    print replace($line, $from, $to, 'g'); 
} 

replace作品像JavaScript的String#replace,從而擴展特殊$序列。

做手工也是可能的,但有點惱人,因爲你基本上要像對待$to爲模板,擴大手工所有$序列(通過其他正則表達式替換EG):

# untested 
$line =~ s{$from}{ 
    my @start = @-; 
    my @stop = @+; 
    (my $r = $to) =~ s{\$([0-9]+|\$)}{ 
     $1 eq '$' 
      ? '$' 
      : substr($from, $start[$1], $stop[$1] - $start[$1]) 
    }eg; 
    $r 
}eg; 

(這確實沒有實現如${1}${2}等。這些都是作爲練習留給讀者桁組)。

此代碼是足夠煩人寫(看看),我更喜歡使用一個模塊一樣Data::Munge的日是某種事情。

+0

不妨提及該模塊。也許是使用'readline'而不是它的操作符'<>'的一個簡單評論?我認爲你更喜歡它在計算上的可讀性和規範性意義(我不抱怨!),但它可能會讓初學者感到困惑(就像在爲什麼這麼做那樣)? – zdim

+0

我從不使用'<' '>',因爲它的特殊語法重載:'<$foo>'意味着'readline($ foo)','<${foo}>'和'<$ foo>'意味着'glob($ foo)'。在所有情況下只寫出'readline'和'glob'就更清楚了(也毫不含糊)。 (獎金:非Perl程序員理解發生了什麼。) – melpomene

+0

我這麼認爲,我完全同意這個觀點。我指的是針對初學者的答案。但是,這不僅僅是一個簡短的評論(這也不是必需的)。 – zdim