2008-09-24 23 views
13

Tôi chắc chắn rằng có một số nhỏ một lớp lót với perl, ruby, bash bất cứ điều gì mà sẽ cho phép tôi chạy một lệnh trong một vòng lặp cho đến khi tôi quan sát một số chuỗi trong stdout, sau đó dừng lại. Lý tưởng nhất, tôi muốn nắm bắt stdout là tốt, nhưng nếu nó sẽ để điều khiển, đó có thể là đủ.Làm cách nào để chạy một lệnh trong vòng lặp cho đến khi tôi thấy một số chuỗi trong stdout?

Môi trường cụ thể được đề cập tại thời điểm này là RedHat Linux nhưng đôi khi cũng cần điều tương tự trên Mac. Vì vậy, một cái gì đó, chung chung và * nixy sẽ là tốt nhất. Không quan tâm đến Windows - có lẽ là một điều * nixy sẽ làm việc dưới Cygwin.

CẬP NHẬT: Lưu ý rằng bằng cách "quan sát một số chuỗi" tôi có nghĩa là "stdout chứa một số chuỗi" không "stdout IS một số chuỗi".

Trả lời

13

Trong Perl:

#!/usr/local/bin/perl -w 

if (@ARGV != 2) 
{ 
    print "Usage: watchit.pl <cmd> <str>\n"; 
    exit(1); 
} 

$cmd = $ARGV[0]; 
$str = $ARGV[1]; 

while (1) 
{ 
    my $output = `$cmd`; 
    print $output; # or dump to file if desired 
    if ($output =~ /$str/) 
    { 
     exit(0); 
    } 
} 

Ví dụ:

[bash$] ./watchit.pl ls stop 
watchit.pl 
watchit.pl~ 
watchit.pl 
watchit.pl~ 
... # from another terminal type "touch stop" 
stop 
watchit.pl 
watchit.pl~ 

Bạn có thể muốn thêm một giấc ngủ trong đó, mặc dù.

+0

Tôi chấp nhận điều này vì a) nó làm việc cho tôi ngay lập tức và b) thật dễ dàng để hack vào để thêm một vài điều (như một truy cập, vv). Nhưng một số giải pháp khác ở đây cũng tốt. –

10

Có một loạt các cách để làm điều này, người đầu tiên mà đến tâm trí là:

OUTPUT=""; 
while [ `echo $OUTPUT | grep -c somestring` = 0 ]; do 
    OUTPUT=`$cmd`; 
done 

Trường hợp $ cmd là lệnh của bạn để thực thi.

Đối với các heck của nó, đây là một phiên bản chức năng BASH, vì vậy bạn có thể gọi đây là một cách dễ dàng hơn nếu nó là cái gì bạn đang muốn gọi từ một vỏ tương tác một cách thường xuyên:

function run_until() { 
    OUTPUT=""; 
    while [ `echo $OUTPUT | grep -c $2` = 0 ]; do 
    OUTPUT=`$1`; 
    echo $OUTPUT; 
    done 
} 

Disclaimer : chỉ được kiểm tra nhẹ, có thể cần phải thực hiện thêm một số thoát vv nếu các lệnh của bạn có nhiều đối số hoặc chuỗi chứa các ký tự đặc biệt.

EDIT: Dựa trên phản hồi từ bình luận của Adam - nếu bạn không cần đầu ra cho bất cứ lý do (tức là không muốn để hiển thị đầu ra), sau đó bạn có thể sử dụng phiên bản ngắn hơn này, với ít sử dụng backticks và do đó ít overhead:

OUTPUT=0; 
while [ "$OUTPUT" = 0 ]; do 
    OUTPUT=`$cmd | grep -c somestring`; 
done 

BASH chức năng phiên bản thêm:

function run_until() { 
    OUTPUT=0; 
    while [ "$OUTPUT" = 0 ]; do 
    OUTPUT=`$1 | grep -c $2`; 
    done 
} 
+0

Bạn thử nghiệm biến bị trống bằng cách chuyển nó vào grep trong một trình bao phụ ??? Bạn không thể nghiêm túc! Mặc dù thực tế là một trình bao có thể kiểm tra điều này một cách dễ dàng (mọi vỏ có thể) và mã đó quá phức tạp, nó cũng cực kỳ kém hiệu quả. – Mecki

+0

Tôi không nghĩ rằng anh ấy đang thử nghiệm nếu nó trống, anh ấy sử dụng grep để xác định xem biến có chứa "somestring" hay không.Nhưng anh ta có thể giảm số lượng sub-shell bằng một nửa bằng cách di chuyển grep đường ống tới dòng sau, nơi nó được gán cho OUTPUT, sau đó WHILE sẽ thử nghiệm một biến trống. –

+0

có vẻ như điều này sẽ luôn thất bại trong khi kiểm tra đầu tiên khi OUTPUT rỗng để bắt đầu? –

1
while (/bin/true); do 
    OUTPUT=`/some/command` 
    if [[ "x$OUTPUT" != "x" ]]; then 
    echo $OUTPUT 
    break 
    fi 

    sleep 1 
done 
+0

Vì vậy, x đây là chuỗi để tìm kiếm? Nếu tôi đang tìm kiếm foo, tôi sẽ làm: nếu [["foo $ OUTPUT"! = "Foo"]]; sau đó ? –

+0

Nếu "x $ OUTPUT" == "x", thì $ OUTPUT trống. Điều này chỉ kiểm tra nếu có * bất kỳ * đầu ra. –

1

EDIT: Câu trả lời ban đầu của tôi giả định rằng "một số chuỗi" có nghĩa là "chuỗi bất kỳ". Nếu bạn cần phải tìm một cái cụ thể, Perl có lẽ là lựa chọn tốt nhất của bạn, vì hầu như không có gì có thể đánh bại Perl khi nói đến kết hợp REGEX. Tuy nhiên, nếu bạn không thể sử dụng Perl vì bất kỳ lý do gì (bạn có thể mong đợi Perl có mặt trong hầu hết các bản phân phối Linux, nhưng không ai ép buộc người dùng cài đặt nó, mặc dù Perl có thể không có sẵn), bạn có thể làm điều đó với sự giúp đỡ của grep. Tuy nhiên, một số các giải pháp grep tôi đã thấy cho đến nay là suboptimal (họ chậm hơn sẽ là cần thiết). Trong trường hợp đó tôi thà làm như sau:

MATCH=''; while [[ "e$MATCH" == "e" ]]; do MATCH=`COMMAND | grep "SOME_STRING"`; done; echo $MATCH 

Thay COMMAND với thực tế lệnh để chạy và SOME_STRING với chuỗi để tìm kiếm. Nếu tìm thấy SOME_STRING trong đầu ra của COMMAND, vòng lặp sẽ dừng lại và in kết quả tìm thấy SOME_STRING.

ĐÁP ORIGINAL:

Có lẽ không phải là giải pháp tốt nhất (Tôi không phải lập trình bash tốt), nhưng nó sẽ làm việc :-P

RUN=''; while [[ "e$RUN" == "e" ]]; do RUN=`XXXX`; done ; echo $RUN 

Chỉ cần thay thế XXXX với cuộc gọi lệnh của bạn, ví dụ hãy thử sử dụng "echo" và nó sẽ không bao giờ trở lại (vì tiếng không bao giờ in bất kỳ thứ gì để xuất chuẩn), tuy nhiên nếu bạn sử dụng "kiểm tra tiếng vọng", nó sẽ chấm dứt cùng một lúc và cuối cùng là in thử nghiệm.

+0

[["e $ MATCH" == "e"]] ít rõ ràng hơn [[-n "$ MATCH"]] –

1
CONT=1; while [ $CONT -gt 0 ]; do $CMD | tee -a $FILE | grep -q $REGEXP; CONT=$? ; done 

Lệnh tee có thể chụp stdout trong một đường ống trong khi vẫn đi qua các dữ liệu trên, và -a làm cho nó thêm vào tập tin thay vì ghi đè lên nó mỗi lần. grep -q sẽ return 0 nếu có kết quả trùng khớp, 1 nếu không và không viết bất kỳ thứ gì để chặn đứng. $? là giá trị trả về của lệnh trước, vì vậy $CONT sẽ là giá trị trả lại của grep trong trường hợp này.

+0

Đơn giản hơn nhiều: cho đến khi $ CMD | tee -a $ FILE | grep -q $ REGEXP; ngủ 1; thực hiện ... Shell sẽ thực thi đường ống nhiều lần cho đến khi trạng thái thoát của grep thành công. Không cần $ CONT hoặc refereneces rõ ràng để $ ?. –

+0

Ồ, và tôi không chắc chắn rằng '-q' là di động; POSIX ủy nhiệm '-s' để thoát với trạng thái 0 (được tìm thấy) hoặc 1 (không tìm thấy). –

0

Một cách đơn giản để làm điều này sẽ

until `/some/command` 
do 
    sleep 1 
done 

Các backticks quanh lệnh làm cho thử nghiệm until đối với một số đầu ra để được trả lại chứ không phải là kiểm tra các giá trị ra của lệnh.

+1

Chỉ kiểm tra trạng thái thoát, chứ không phải stdout; do đó không trả lời câu hỏi. –

+1

Bạn đã bỏ lỡ các dấu backticks xung quanh lệnh mà thay đổi những gì cho đến khi đánh giá. Tôi sẽ thêm bản chỉnh sửa để làm rõ điều này. –

+1

Vòng lặp cho đến khi chạy/một số/lệnh và thực thi đầu ra của nó, kiểm tra trạng thái thoát. Điều này vốn đã bị hỏng. –

4

grep -c 99999 sẽ in 99999 dòng bối cảnh cho trận đấu (Tôi giả định này sẽ có đủ):

while true; do /some/command | grep expected -C 99999 && break; done 

hoặc

until /some/command | grep expected -C 9999; do echo -n .; done 

... điều này sẽ in một số dấu chấm thoải mái cho thấy tiến trình .

7

Tôi ngạc nhiên tôi đã không nhìn thấy một ngắn gọn Perl one-liner đề cập ở đây:

perl -e 'do { sleep(1); $_ = `command`; print $_; } until (m/search/);' 

Perl là một ngôn ngữ thật sự tốt đẹp cho các công cụ như thế này. Thay thế "lệnh" bằng lệnh bạn muốn chạy liên tục. Thay thế "tìm kiếm" bằng những gì bạn muốn tìm kiếm. Nếu bạn muốn tìm kiếm thứ gì có dấu gạch chéo, hãy thay thế m/search/ bằng m#search chuỗi với /es#.

Ngoài ra, Perl chạy trên nhiều nền tảng khác nhau, bao gồm Win32 và thao tác này sẽ hoạt động ở bất cứ đâu bạn có cài đặt Perl. Chỉ cần thay đổi lệnh của bạn một cách thích hợp.

Các vấn đề liên quan