お世話になります。ご指南いただければ幸いです。
Perl初心者です。
自前のサイトで会員ログファイルから各ユーザーの最終ログインを抽出したいと思っています。
ログファイルを作るところまでは何とかなったのですが、抽出する方法が見当もつきません。
(ログファイルではない閲覧用のファイルが作れればありがたいですが抽出するロジックがわかりません。)
現在は以下のようなログファイルができています。
ユーザーA,2016-08-28 12:44:14
ユーザーA,2016-08-25 12:44:55
ユーザーB,2016-08-28 12:45:47
ユーザーC,2016-08-27 12:45:54
ユーザーC,2016-08-28 12:45:54
ユーザーB,2016-08-28 12:45:54
ユーザーA,2016-08-28 12:45:54
それぞれユーザーA-Cさんの最終ログイン行を抽出したいのですが、
ご教授いただけませんでしょうか。
ログファイル上の時刻は以下のように取得しています。
my ($sec, $min, $hour, $day, $mon, $year) = localtime(time);
my $now = sprintf('%04d-%02d-%02d %02d:%02d:%02d', $year + 1900, $mon + 1, $day, $hour, $min, $sec);
以上です。よろしくお願いします。
No.8ベストアンサー
- 回答日時:
#7の方が指摘されていますように
my $user = split(',',$_); #カンマで分割した最初のフィールドを取得
を
my ($user) = split(',',$_); #カンマで分割した最初のフィールドを取得
に変えてください。
(#1で投稿したスクリプトもそうなっています)
一応、解説しておきますと、
($変数1,$変数2,....) = split(....);
の場合は、分割された文字列の配列を順番に$変数1,$変数2、・・・に格納します。
これが通常の使い方です。
従って、my ($user) = split(...)は、分割された文字列の最初のものを$userに格納します。
一方、
$変数 = split(...);
は、分割された文字列の配列の数(今回は4)を$変数に格納します。
(つまり、usrlog.cgi の1行はカンマで4つのフィールドに区切られているはずです。)
この使い方は、あまり行われません。(分割された数を求めたい場合は別ですが・・・)
従って、splitの左辺は、
($var1,$var2,...) = split(....);
とするか
@var = split(...);
のようにするかのいずれかが一般的です。
@varの場合は、分割された結果を配列として@varの格納するので、
それを個々に参照する場合は、$var[0],$var[1]のようになります。
今回のケースでは
$var[0]にユーザー名、
$var[1]に時刻が格納されるようになります。
いつもご丁寧にありがとうございます。よく分かりました。
また改めて自分でも調べて恥ずかしながら納得しているところです。
この後、ユーザー名、時刻を抽出するように改造するつもりですので、しばらく自力でチャレンジしてみようと思います。
この数日間、ご教授いただいてありがとうございます!m(__)m
No.9
- 回答日時:
結論は #8 の通り (もうちょっと効率的にはできるけど大した意味はない) なんだけど, ちょっと指摘しておきたい.
#7 の「split が何を返すのか確認すること.」に対して「splitのWEBリファレンスを読んでいます。」の結果として「split(',',$_)」と「split /,/, $_」の「双方で処理を行ってみた」んだよね. だとすると, 「splitのWEBリファレンス」を読んだことにより, この 2つで異なるものが返ると判断しないとおかしい. では, その「splitのWEBリファレンス」では「split が何を返すのか」についてどのように書いてあるのでしょうか? そして, それを読んでどう考えた結果としてこの 2つで異なるものが返ると判断したんでしょうか?
ご指摘ありがとうございます。
実際調べた(http://www.perlplus.jp/ 他)ときは「split /,/, $_」の説明ばかりで「split(',',$_)」は見つけられずに、実際動かしてみて見た目の結果で判断したのですが、ご指摘いただいた通り「 2つで異なるものが返ると判断しないとおかしい」ということに気づきませんでした。というか正直に申し上げて思い至らなかったというのが本音です。
皆さんのお知恵を借りながらそのようなところに思いが及ばず、逆に申し訳ないです。コメントしていただいてありがとうございました。
No.7
- 回答日時:
あぁ,
my $user = split(',',$_);
がおかしい.
split が何を返すのか確認すること.
ご指摘ありがとうございます。splitのWEBリファレンスを読んでいます。
split(',',$_) or split /,/, $_;
双方で処理を行ってみましたが、見た目の結果は変わらずです。アドバイス頂いた意図をよく理解していないのかもしれません。。。。。
ログの読み出しについては、
$user = split(',',$_)
まず1行ずつ[,]で分割し、各要素を特殊変数$_に配列として格納
$users{$user} = $.;
その後ユーザー毎のファイルハンドルの現在ある位置の行数を$users{$user} に格納
これをwhileまたはforeach文ですべて読みだす(行数をつけ、配列として格納)という流れと理解しています。
No.6
- 回答日時:
>結果は
>user_name=4 line_no=35
>と1行だけ表示されます。
ログを読み込んで、その結果をファイルへ出力していると思いますが、
その部分のソースを提示していただけますでしょうか。
必ず、コピー&ペーストで行ってください。(手入力で転記すると、転記で誤りがあった場合、原因が特定しにくくなります)
毎夜毎晩ありがとうございます。感謝です。
my %users = ();
open(FH,"$cf{logdir}/usrlog.cgi") or error("open err: usrlog.cgi");
while(<FH>){
my $user = split(',',$_); #カンマで分割した最初のフィールドを取得
$users{$user} = $. #その行番号を記憶
}
#全ユーザー名と行番号を印字
open(FH,"> $cf{logdir}/usrlog2.cgi") or error("open err: usrlog2.cgi");
foreach my $user (keys %users){
printf FH "user_name=%s line_no=%d\n",$user,$users{$user};
}
こちらになりますm(__)m
No.5
- 回答日時:
単に %s とか %d とかで出力するだけなら printf を使うよりも
print FH "user_name=$user line_no=$users{$user}\n";
と埋め込んじゃった方が簡単じゃないかなと思ったりする.
今どきの Perl では 3引数の open を使った方がいいかなと思ったりもするけど.
No.3
- 回答日時:
こちらで、確認していないので、推測になりますが、明らかにおかしいところは、以下の箇所です。
my %users = open(FH,"$cf{logdir}/usrlog.cgi") or error("open err: usrlog.cgi");
while(<>){
上記を
my %users = ();
open(FH,"$cf{logdir}/usrlog.cgi") or error("open err: usrlog.cgi");
while(<FH>){
に変えてください。
一応、解説しておきますと、
my %users = ();・・・連想配列の初期化
open(FH,"$cf{logdir}/usrlog.cgi") or error("open err: usrlog.cgi"); ・・ファイルのオープン
openの結果は、真、または偽を返すので、
my %users = open(FH,....) ・・・とすると、%usersを初期化したことになりません。
while(<FH>){ は、オープンしたファイルについて、先頭から読む場合です。
while(<>){ は、コマンドラインで与えられた第1引数(及び第2引数以降)をファイル名として先頭から読む場合です。
私が#1で投稿したのは、コマンドラインで第1引数にファイル名を与えたので、while(<>){ としました。
今回は、open(FH,"$cf{logdir}/usrlog.cgi") でオープンしたファイルを読み込むので、
while(<FH>){ とします。
No.1
- 回答日時:
以下のようなスクリプトを作成します。
(sample.plとします。)-----------------------------------------------
my %users = ();
while(<>){
my($user) = split(',',$_); #カンマで分割した最初のフィールドを取得
$users{$user} = $. #その行番号を記憶
}
#全ユーザー名と行番号を印字
foreach $user (keys %users){
printf("user_name=%s line_no=%d\n",$user,$users{$user});
}
-------------------------------------------------------
perl sample.pl ログファイル
と入力すると、結果が表示されます。
以下、提示されたログファイルを読み込んで実行した結果です。
user_name=ユーザーA, line_no=7
user_name=ユーザーB, line_no=6
user_name=ユーザーC, line_no=5
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- CGI perlで書いたcgiでsqliteの使い方を教えてください 2 2023/05/08 21:29
- その他(クラウドサービス・オンラインストレージ) Zoomの代替ホストによる会議の同時開催は可能でしょうか 1 2023/06/17 13:29
- SQL Server SQLのクエリの書き方 1 2022/03/29 23:06
- Java jdk17.06のインストーラーが起動しない 1 2023/03/27 21:58
- その他(プログラミング・Web制作) Windowsのマクロプログラムで、こんなことできますか? 3 2022/06/28 14:30
- その他(インターネット接続・インフラ) windows10or11の別ユーザーでの設定について 1 2022/08/04 16:21
- Visual Basic(VBA) 複数ファイルのデータの統合について 12 2022/05/14 12:03
- Chrome(クローム) Chromeのログインしているページからブックマークが消えて、ユーザー1に移動してしまいました 2 2023/06/30 17:36
- 教えて!goo 教えてgooでは、利用者が少ないため下記のように、2人のユーザーが互いに質問し答えてはまた質問し、を 3 2023/06/12 20:04
- UNIX・Linux Ubuntu on Xorgのログインについて 2 2023/08/10 15:16
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
perlのflock関数でロックをかけ...
-
close()で例外が投げられる理由
-
巨大ファイルの行をを逆順に並...
-
open中のファイルをrename
-
PICでFatFsでオープンした内容...
-
perlで複数のファイルの処理に...
-
window.open でのファイル指定方法
-
awkスクリプトでダブルクォーテ...
-
VBAでCSVファイルの特定行を書...
-
VBAでCSVファイルを途中行まで...
-
batファイルでrenameができませ...
-
【エクセル】改行無しテキスト...
-
Perlで特定文字列から特定文字...
-
ExcelをCSV書き出す場合のシー...
-
Perl で ディレクトリ及びサブ...
-
バッチファイルの作り方(CSV→...
-
vba dir の相対パス
-
Perlによるディレクトリ内の連...
-
パスから最後のディレクトリだ...
-
VBAで巨大なファイルの途中から...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
close()で例外が投げられる理由
-
perlを用いた特定文字列間の抽...
-
改行コードが勝手に
-
perlのflock関数でロックをかけ...
-
perlで複数のファイルの処理に...
-
perlでファイル内の文字列を置...
-
ハッシュにファイルハンドル
-
die関数のエラー出力先について
-
AutoCADのスクリプト
-
パスワード自動生成スクリプト...
-
パイプをopenして/usr/bin/grep...
-
ファイルから読み込んだ文字を
-
文字列比較がどうしてもできま...
-
perlでURLから画像ファイルを作...
-
巨大ファイルの行をを逆順に並...
-
telnet接続
-
cgiの投票回数制限設定について...
-
Perl変数から外部プログラムの...
-
PICでFatFsでオープンした内容...
-
データファイル中のデータをラ...
おすすめ情報
ありがとうございます。日々学習を続けていこうと思います。
頂いた回答をもとにKENTWEBさんのWEBPATIOを改造しています。
メイン実行ファイルの中にサブルーチンで動かし、実行結果を外部ファイルに書き出そうとしています。
ログをusrlog.cgiに保存し、そのログから抽出したものをusrlog2.cgiに保存しようとしていますが、うまくいきません。
sub usr_logrec
{
my ($sec, $min, $hour, $day, $mon, $year) = localtime(time);
my $now = sprintf('%04d-%02d-%02d %02d:%02d:%02d', $year + 1900, $mon + 1, $day, $hour, $min, $sec);
open(FH,">> $cf{logdir}/usrlog.cgi") or error("open err: usrlog.cgi"); #追加モード
print FH "$au{name},$now,$in{id},$au{rank}\n"; #書き込み
close(FH); #ログ保管完了
my %users = open(FH,"$cf{logdir}/usrlog.cgi") or error("open err: usrlog.cgi");
while(<>){
my $user = split(',',$_); #カンマで分割した最初のフィールドを取得
$users{$user} = $. #その行番号を記憶
}
#全ユーザー名と行番号を印字
open(FH,"> $cf{logdir}/usrlog2.cgi") or error("open err: usrlog2.cgi");
foreach my $user (keys %users){
print FH "user_name=%s line_no=%d\n",$user,$users{$user};
}
close(FH);
}
というように処理を行っています。
もしアドバイスいただけるなら大変ありがたいですm(__)m
コメントありがとうございます。
ログをusrlog.cgiに保存はできており、usrlog2.cgiにもアクセスができております。
usrlog2.cgiに結果を書き込むのがおかしい様で、usrlog2.cgi内には結果というよりも
プレーンテキストで
user_name=%s line_no=%d
だけが書き込まれます。原因がファイルの扱いなのか、変数の扱いなのか、手順なのか
そのあたりがわからず困っています。
期待した結果はNo1様の頂いた内容で
user_name=ユーザーA, line_no=7
user_name=ユーザーB, line_no=6
user_name=ユーザーC, line_no=5
を、外部ファイル(usrlog2.cgi)に書き込みたいと思います。
ご報告です。
皆様にご指南頂きながら組み込んで、試行錯誤しました^^;
以下のログに対して
ユーザーA,2016-08-28 12:44:14
ユーザーA,2016-08-25 12:44:55
ユーザーB,2016-08-28 12:45:47
ユーザーC,2016-08-27 12:45:54
・
・
・
35行が最新
結果は
user_name=4 line_no=35
と1行だけ表示されます。
%sなのでユーザー毎が出るのかと思いながら色々やってみましたが
結果が数値表示となり変わらずです。やはり勉強が足りないことを痛感しました。
何か方策があればご教授いただければ幸いです。
何度も申し訳ありません。
ご指南ありがとうございます。当初の形で結果を得ることができました!
頂いた内容をもとに改めて学習をして、最終は、
ユーザーA,2016-08-25 12:44:55 (ユーザー毎の最新)
ユーザーB,2016-08-28 12:45:47
ユーザーC,2016-08-27 12:45:54
というような形で結果が得れるようにチャレンジしてみます。
ありがとうございます!