簡體   English   中英

從Perl腳本運行Unix命令

[英]Run Unix command from a perl Script

我正在嘗試在服務器上執行ssh,然后執行grep以獲取日志文件中不同錯誤的計數。 一旦有了這些詳細信息,就將它們記錄到CSV文件中。 但是,當我嘗試運行grep命令時,出現錯誤。

#!/usr/bin/perl
my $addr = "user\@servername";
my $text = qq|Internal Server Error|;
my $remote_path = "/data/logs/error";
my $cmd = `ssh $remote_addr "grep -a $text $remote_path | awk -F " " '{print $4}' | sort -nr | uniq -c | sort -nr 2>/dev/null"`;
print $cmd;

但是我在運行腳本時遇到錯誤

grep: Internal: No such file or directory
grep: Server: No such file or directory
grep: Error: No such file or directory

有沒有建議我們如何在shell腳本中做到這一點。

首先,為了避免引人入勝的噩夢和大量的shell注入機會,我建議使用一個模塊,例如String :: ShellQuote

然后,我看不到您需要所有這些外部工具,而如此長的管道既棘手又昂貴。 它為Perl中確實完成的工作調用了許多程序,並且需要非常精確的語法。

除了ssh -ing之外,外部工具可能對此還有好處的另一件事是,在文件很大的情況下,使用grep提取感興趣的行(否則您可以將其讀取為標量)。

use warnings;
use strict;
use feature 'say';

use List::Util qw(uniq);  # in List::MoreUtils prior to module v1.45
use String::ShellQuote qw(shell_quote);

my $remote_addr = ...
my $remote_path = ...
my $text = 'Internal Server Error';

my $remote_cmd = shell_quote('grep', '-a', $text, $remote_path);
my $cmd = shell_quote('ssh', $remote_addr, $remote_cmd);

my @lines = qx($cmd);
chomp @lines;

# Process @lines as needed, perhaps
my @result = sort { $b <=> $a } uniq map { (split)[3] } @lines;
say for @result;

一旦運行外部命令,就有許多選擇。 首先考慮使用模塊。 它們都大大簡化了工作,尤其是通過錯誤檢查,並且通常更可靠,而有些還使更困難的工作更易於管理。

IPC :: System :: Simple的示例

use IPC::System::Simple qw(capturex); 

my @lines = capturex('ssh', $remote_addr, $remote_cmd);

由於ssh在運行時執行命令,因此它不需要外殼(對於該部件),因此使用capturex 請參閱文檔以獲取更多選項以及如何檢查錯誤。

從簡單到更強大的其他一些選項是Capture :: TinyIPC :: Run3IPC :: Run

欲了解更多有關這一切看到組裝環節這篇文章 (和尋找更多)。


我看不到一個需要運行的管道,因為它代表但如果有一個(留在遠程主機上的?)然后形成如上命令,然后組裝完整的管道

my $cgrep = shell_quote('grep', '-a', $text, $remote_path);
my $cawk  = shell_quote('awk', '-F', ' ', '{print $4}');
my $csort = shell_quote('sort', '-nr');
my $cuniq = shell_quote('uniq', '-c');

my $remote_cmd = "$cgrep | $cawk | $csort | $cuniq | $csort 2>/dev/null";

請注意,不應引用所需的shell功能( |和重定向)。

awk塊中僅有的空間可能看起來很尷尬,但是由於它逃脫了,所以它會朝-F 對我來說,這是在shell管道中運行外部程序的另一個麻煩跡象。 多虧了查爾斯·達菲Charles Duffy)的評論,我無法弄清那空余的空間。

在這種情況下,流水線的sortuniq部分只能作為一個字符串鍵入,因為它只是程序名稱和選項,但是一旦進行更改或任何變量進入其中,就變得很棘手。 因此,我使用shell_quote來保持一致性並用作模板。


據報告模塊丟失,難以獲得。 然后逃避需要逃避的內容(直到您弄清楚如何獲取模塊)。 在這種情況下,幾乎沒有什么要修復的,但是該位可以作為常見的復雜循環流水線的示例。

帶有$text的字符串需要到達grep這樣的一個字符串。 由於它通過外殼,它將被空間分解成單詞,因此我們需要保護(引用/轉義)那些空間。 別忘了,我們還需要首先通過Perl的解析規則將其放入外殼。

單程

my $text_quoted = q(') . quotemeta($text) . q(');

quotemeta還會引用其他所有內容。

我們還應該保護文件名模式,因為它可能依賴於shell元字符(例如*

my $remote_path_quoted = quotemeta $remote_path;

但同樣,您必須檢查每種情況是否合適。

注意如果在這些命令中插入了任何動態生成的變量(計算所得,來自用戶...),則需要對它們進行驗證,並仔細進行轉義和引用

現在您的管道應該可以工作了(在我的模擬測試中可以)

my $cmd = "ssh $remote_host grep -a $text_quoted $remote_path_quoted"
    . q( | awk F ' ' '{print $4}' | sort -nr | uniq | sort -nr 2>/dev/null);

可以將它們分解為自己變量中的明智組件,等等,但是我真的不建議您使用此類手動修補的解決方案。

我建議只使用第一部分(ssh + grep),其余部分在Perl中進行,就像答案的主要部分一樣。 然后安裝這些模塊並切換到它們。

沒有(很多)庫,沒有任何主要的計算工具可以工作,並且每個生產安裝都包含許多“附加”內容。 隨着對更多庫的需求的出現,它們被安裝。 為什么與Perl必須有所不同? 是的,您只能使用內建函數來執行此操作,但這可能要困難得多。


一個很好的原因是在文件很大時使用系統sort ,因為它不必一次加載整個文件,而且不必因為速度而加載整個文件。 但是,在此管道中,它通過管道饋送數據並重復調用,因此這些優點不適用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM