質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Q&A

解決済

2回答

536閲覧

perlにてlastでwhileを抜け出したい。

TAKE147

総合スコア20

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

0グッド

0クリップ

投稿2022/09/01 05:18

編集2022/09/02 05:45

前提

はじめて質問させていただきます。よろしくお願いします。
perl言語でtcpdumpの出力をファイルハンドルから読み込み、SYNフラグか、FINフラグに応じて処理をしていくプログラムを書いています。
その際while文とlastを使って、コマンド実行時に
./プログラム名 -n=10
のように指定した回数読み込みを行うのですが、lastが行われずにwhileを抜け出せません。

実現したいこと

-$nで指定した回数処理を行ったらすぐにwhile文を抜け出せるようにしたい

実行結果

出力は以下のようになります。 ---------------------------------- tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes Flag[S]! Flag[F]!! $count = 1 (中略) Flag[S]! Flag[F]!! $count = 10 $count=$n! -----------------------------------

特に知りたいこと

上記出力の最後、$count = 10の後に、lastが行われる条件 $count == $n が満たされていることを示す $count=$n!が表示されているにもかかわらず、lastによってwhile文を抜け出せていません。(もし抜け出していたらclose(PKT)とprint"finish!\n"が実行されているはず)どうして抜け出せないのかがわかりません。

該当のソースコード

perl

1#!/usr/bin/perl -sw 2 3$count = 0; 4 5#ループ回数が指定されていないとき、10回とする 6unless ($n) { 7 $n = 10; 8 print "\$n=$n\n"; 9} 10 11# コマンドの出力をパイプでファイルハンドルへ渡す 12open (PKT, "tcpdump -l -i eth0 -tt -nn '((host 192.168.122.11) and (tcp) and (src port 80) and (tcp[tcpflags] & (tcp-syn|tcp-fin) != 0))'|"); 13 14# ファイルハンドルからtcpdumpの結果を読み込む 15while (<PKT>) { 16 print "\n"; 17 18# フラグがSINのとき 19 if(/^(\d+)\.(\d+).*> \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.(\d+).*(Flags \[S\.?\]).*seq (\d+)/) { 20 print "Flag[S]!\n"; 21# フラグがFINのとき 22 }elsif(/^(\d+)\.(\d+).*> \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.(\d+).*(Flags \[F\.?\]).*seq (\d+)/){ 23 print "Flag[F]!!\n"; 24 $count++; 25 print "\$count = $count\n"; 26 } 27 28 if ($count == $n){ 29 print "\$count=\$n!\n"; 30 last; 31 } 32} 33 34close (PKT); 35print"finish!\n";

試してみたこと

デバッグにてステップ実行を行いました。

($count=9までは省略) --------------------------------------------- main::(./trash.pl:16): print "\n"; DB<1> s main::(./trash.pl:19): if(/^(\d+)\.(\d+).*> \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.(\d+).*(Flags \[S\.?\]).*seq (\d+)/) { DB<1> s main::(./trash.pl:23): print "Flag[F]!!\n"; DB<1> s Flag[F]!! main::(./trash.pl:24): $count++; DB<1> s main::(./trash.pl:25): print "\$count = $count\n"; DB<1> s $count = 10 main::(./trash.pl:28): if ($count == $n){ DB<1> s main::(./trash.pl:29): print "\$count=\$n!\n"; DB<1> s $count=$n! main::(./trash.pl:30): last; DB<1> s main::(./trash.pl:34): close (PKT); DB<1> s s ----------------------------------------------- (ここで何も起きなくなるが、この状態の時にパケットを送ると) ----------------------------------------------- tcpdump: Unable to write output: Broken pipe main::(./trash.pl:39): print"finish!\n"; DB<1> finish!

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2022/09/02 02:04

実行しているのが、質問文のコードと違うとかですかね。
TAKE147

2022/09/02 02:10

回答ありがとうございます!確認したところソースコード、実行結果ともに掲載した通りです。
otn

2022/09/02 03:04 編集

> lastによってwhile文を抜け出せていません。 具体的な現象が書かれていませんが、$count のカウントアップが延々と続くと言うことですかね?
TAKE147

2022/09/02 03:15

いいえ、-n=10 で指定した回数より多くパケットを送る(FINフラグを受け取って$countが10より多くなる)とlastが実行されてプログラムは終了します。 ただ実現したいことは$countが10になったとき、つまり指定した回数処理を行ったらlastが行われて欲しいのです。説明が下手ですみませんが伝わりましたか?
KojiDoi

2022/09/02 04:32

デバッガのステップ実行でループ制御がどう働いているか見てみましたか?
TAKE147

2022/09/02 04:37

やり方すら知らないレベルでして、やっていないです。今から調べてやります!
TAKE147

2022/09/02 04:54

ステップ実行を行いました。こうして見るとlast自体は行われていて、ファイルハンドルのcloseも行われているけど最後のprint"finish!\n"が行われず、次のステップに進まない(デバッグでsを入力しても何も起こらない)みたいです。
KojiDoi

2022/09/02 04:57

print出力のバッファリングが効いているだけという可能性はありませんかね。
otn

2022/09/02 05:17

>FINフラグを受け取って$countが10より多くなる)とlastが実行されてプログラムは終了します。 と、 > ただ実現したいことは$countが10になったとき、つまり指定した回数処理を行ったらlastが行われて欲しいのです。=> 行われていないと言うことか? は矛盾しますが、どっちが正しいのでしょうか?頭の中を整理しましょう。 > print出力のバッファリングが効いているだけという可能性はありませんかね。 プログラムの末尾なのでそれは無いと思います。 > 確認したところソースコード、実行結果ともに掲載した通りです。 という回答なので。 まあ、確認が間違っていて、実はプログラムが違うという可能性もかなりあると思いますが。
TAKE147

2022/09/02 05:52

すみませんlastは行われていることがデバッグによりわかりました。ある意味当初の解決したい点は解決した(最初からしていた)と言えますが、私の実装したいことは$count=10になった直後、何も起こらなくなるのを防いですぐに print"finish!\n"が行われることです。
otn

2022/09/02 07:14 編集

実行結果欄にある、$count=$n! の表示の後、プログラムは終了するのですか?それともずっと実行中のままなのでしょうか?「whileから抜け出せてない」と思っていたと言うことは、実行中のままですかね。
otn

2022/09/02 07:13

実行は、具体的にどんな手段で行っていますか? 端末で起動しているシェルに対して、ファイル名を打ち込む?
TAKE147

2022/09/02 07:15

実行中のままです。その状態から新しいパケットを受け取ると、 tcpdump: Unable to write output: Broken pipe finish! となります。
TAKE147

2022/09/02 07:26

実行は仮想マシン(サーバ)の端末で行い、物理マシン(クライアント)からhttperfコマンドで仮想マシンのipアドレスに対して、コネクション数を例えば10のように指定して行っています。
TAKE147

2022/09/02 07:48

Ctrl+Cコマンドを送ることや、kill関数、exit関数を使うなどいくつか考えたのですが、実装できなかったり、finish!表示前に終了してしまったりと、うまくいかないみたいなので、この方法では難しいと判断しました。 そこでtcpdumpの-cオプションでパケットを受け取る回数を$nで指定して決まった回数tcpdumpを行う方法でやることにしました。 途中で打ち切るような形で申し訳ありませんが、これにて解決ということにさせていただきます。 アドバイスをくださった otn様、KojiDoi様ありがとうございました。
otn

2022/09/02 09:50

> 実行は仮想マシン(サーバ)の端末で行い、 具体的にはどのように行っているのですか?
TAKE147

2022/09/02 09:54

端末(コマンドプロンプトみたいなの)から ./プログラム名 -n=10 のようにして実行します。 実行したらtcpdumpが行われるので、クライアントからhttperfでパケットを送ります。
guest

回答2

0

ベストアンサー

入力パイプのクローズを明示的にやったことがなかったので、気づいてませんでしたが、パイプのクローズはプロセスの終了を待つので、tcpdumpの場合は出力が発生してBroken pipeで止まるまで待ち続けます。
(出力パイプのクローズは普通はそれで相手プロセスが終了するが、入力のクローズで直ちに終了しない場合は同じく終了を待つことになる)

クローズ前にプロセスを終了するくらいでしょうか。

Perl

1~~ 2$pid = open (PKT, "~~~|"); 3~~ 4kill(15,$pid); 5close (PKT); 6print"finish!\n";

投稿2022/09/02 10:55

otn

総合スコア84557

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TAKE147

2022/09/05 04:36

確かにこの方法でうまくできました。ありがとうございました。
guest

0

$count == $n を満たすと、新たなパケットを受け取る以外の方法では(少なくとも私が調べた限り)次に進まないことがわかりました。
そのためやり方を変えて、while文を制御するのではなくtcpdumpで制御することにしました。コマンドをファイルハンドルへ渡す部分に
オプション -c $n を追記して、n個パケットを受け取ったら終了という方法にしました。

投稿2022/09/02 07:49

TAKE147

総合スコア20

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問