#author("2022-04-11T13:22:01+00:00","","") #author("2023-05-20T14:34:29+09:00","","") #topicpath #contents(); #ls2(Lang/Perl/); //////////////////////////////////////////////////////////////////////////////// * perldoc [#cefb1795] - [[perldoc.jp>https://perldoc.jp/]] //////////////////////////////////////////////////////////////////////////////// * システムにおける各データ長を調べる [#e6f41a10] - 以下のコマンドを実行することで、各データ型のデータ長を取得出来る。 $ perl -V:{short,int,long{,long}}size - 実行結果例 shortsize='2'; intsize='4'; longsize='8'; longlongsize='8'; //////////////////////////////////////////////////////////////////////////////// * 1sec 未満の sleep [#s1414a2e] - 1sec未満の sleep をするには、sleep ではなく、select を使用する。 select undef, undef, undef, 0.5; # 0.5sec の sleep //////////////////////////////////////////////////////////////////////////////// * Perlでハッシュ [#b6565f40] //============================================================================== ** hashを宣言(?)する [#o111d4f1] my %aaa = ( 'ip' => '192.168.0.1', 'host' => 'foo' ); //============================================================================== ** hashの中身を参照する [#r9e8457e] $aaa{'ip'} //============================================================================== ** hashを配列にする [#k47bc7e1] - hashを配列にするには、配列の要素となるhashを1個、ループ内において my で宣言しておき、これに値を入れ、配列に入れる。 for ($i = 0; $i <= $#A_ARRAY; $i++) { my %aaa = ( 'ip' => "192.168.0.$i", 'host' => "host_$i" ); # <--- ループ内で my で宣言するのがミソ $aaa{'ip'} = $new_ip; # $HOSTS[$#HOSTS+1] = \%aaa; push (@HOSTS, \%aaa); } //============================================================================== ** hash の配列を宣言時に初期化 [#y4b37e34] - hash の配列を宣言する。 - 例: my (@HOSTS) = ( { ip => '192.168.1.34' , host => 'ikura.example.com' }, { ip => '192.168.1.35' , host => 'tai.example.com' }, { ip => '192.168.1.36' , host => 'shake.example.com' }, ); //============================================================================== ** 配列の中身を見る [#xc01f65a] - 上で作ったhashの配列の中身を見るには、以下のようにすればOK。 foreach $h (@HOSTS) { print "ip: $h->{'ip'}\n"; } //============================================================================== ** 配列に入れたhashの中身を更新する [#daf96c11] - たとえば、 foreach $x (@XXX) { my (%host) = ('IP' => "0.0.0.0", 'host' => "host_$x" ); # $HOSTS[$#HOSTS+1] = \%host; push (@HOSTS, \%host); } のようにして作ったhash配列 @HOSTS に対して、その$i番目の中身を更新するには (1) : $HOSTS[$i]->{'IP'} = "192.168.0.1" # これでは更新出来ない。 (2) : my (%tmp) = $HOSTS[$i]; $tmp{'IP'} = "192.168.0.1"; $HOSTS[$i] = \%tmp # これでなら更新出来る。 //============================================================================== ** hashを別のhashにコピー [#t1bc7fcc] - 丸ごとコピー出来る。 %aaa = ( 'val1' => 10, 'val2' => 100 ); %bbb = %aaa; //============================================================================== ** hash の中身を全て取り出す [#z11a7aa3] - key() 関数を使うと、引数に渡した hash の中身の key を取り出すことが出来る。これを使って hash に格納されている値を全て取り出すことが出来る。 my (@key) =keys (%ENV); foreach $k (@key) { print "$key=[$ENV{$k}]\n"; } //============================================================================== ** 参考URI [#c21d8467] - [[Perlについて構造体配列を宣言&使いたい。>http://www.rescue.ne.jp/CGI-BBS/cgi/perl/20030507180401.shtml]] - [[cの構造体vsPerlのハッシュ>http://www.geocities.co.jp/SiliconValley-Sunnyvale/6128/perl/structhash.html]] - [[Class::Struct>http://www2u.biglobe.ne.jp/~MAS/perl/waza/struct.html]] //////////////////////////////////////////////////////////////////////////////// * 配列要素 [#array] //============================================================================== ** 基本事項 [#h1e888f5] - 配列の要素数 -- 配列をスカラに代入すると得られる。 $num = @array; # 配列 @array の要素数が $num に入る -- &color(#ff0000){ ローカル変数へ格納する際の注意:}; --- NG例 my ($num) = @array; # 配列 @array の要素数ではなく先頭の要素が入ってしまう。 --- 予め my ($変数) とし、文を改めてから代入すれば正しく要素数が入る my ($num); $num = @array; # 配列 @array の要素数が $num に入る - 配列の最後のindex番号 -- &color(#ff0000){$#};配列名とすると、その配列の最後のindex番号が得られる。 $#array # 配列 @array の最後のindex番号を表す。 //============================================================================== ** sort [#gcd11b25] - 辞書順 @dest = sort { $a cmp $b } @src; # 順方向 @dest = sort { $b cmp $a } @src; # 逆方向 - 値順 @dest = sort { $a <=> $b } @src; # 順方向 @dest = sort { $b <=> $a } @src; # 逆方向 - hash配列も特定の値でsort出来る @dest = sort { $a->{'VarName'} <=> $b->{'VarName'} } @src; # 順方向 //============================================================================== ** 挿入・削除 [#u287eeb9] - 配列の最後に要素を挿入 push (@dst, $add) @dst: 挿入先配列, $add: 挿入する値 - 配列の後ろに別の配列を繋げる --- これも push() でOK。 push (@dst, @add); @dst: 挿入先配列, @add: 挿入する配列 - 配列の最後から要素を削除 $deleted = pop (@src) @src: 取り出し元配列。実行後は $deletedが取り除かれた配列になっている $deleted: @srcの最後から取り出した要素 - 配列の先頭に要素を挿入 unshift (@src, $add) @dst: 挿入先配列, $add: 挿入する値 - 配列の先頭から要素を削除 $deleted = shift (@src) @src: 取り出し元配列。実行後は $deletedが取り除かれた配列になっている $deleted: @srcの先頭から取り出した要素 ※arrayの要素がhashであっても大丈夫。 //============================================================================== ** 配列の初期化 [#m8f2642e] - 配列から要素を全て削除する @array = (); //============================================================================== ** 多次元配列 [#s4495e15] - 多次元配列としての初期化 @ar = ( [ "a", "b", "c", ], [ 1, 2, 3, ], ["Q", "W", "E", ], ); //////////////////////////////////////////////////////////////////////////////// * 時間関数 [#ef88b2b5] //============================================================================== ** 経過日数の計算 [#jeaffa25] - Time::Local を利用する。 - 例:現在から丁度10日と2時間後の日時を得る use Time::Local; # timelocal() my($sec,$min,$hour,$mday,$mon,$year); # 普通は localtime(time) の返り値を代入するが、ここは例示なので具体的な日付けで ($year,$mon,$mday,$hour,$min,$sec) = (2009,01,01,12,00,23); #2009年1月1日12時0分23秒 # perlの常識ながら、$monは0オリジン、$yearは1900オリジンなので、人間が使う値が入っている場合には、当然引き算が必要 my ($time_local) = timelocal ($sec,$min,$hour,$day,$mon-1,$year-1900); # 上で得られた $time_local は、基準日時(1970年01月01日で0時0分0秒)からの秒数になっている。 # であるから、ここから更に「10日と2時間」を秒数に換算したものを加えると、 $time_local += (10 * 24 * 60 * 60) + (2 * 60 * 60); # 以下で得られる $res_* に入る値が、求める日時である。 my ($res_sec,$res_min,$res_hour,$res_mday,$res_mon,$res_year,$res_wday,$res_yday,$res_isdst) = localtime($time_local); //============================================================================== ** 秒未満の時間を取得する [#xd20294a] - Time::HiRes の gettimeofday を使用する use strict; use warnings; use Time::HiRes "gettimeofday"; my ($current_time) = &get_time_stamp (); sub get_time_stamp { my ($epoc_sec, $micro_sec) = gettimeofday(); my ($sec, $min, $hour, $day, $mon, $year) = localtime($epoc_sec); $year += 1900; $mon++; my ($time_stamp) = sprintf("%04d-%02d-%02d %2d:%2d:%2d.%d", $year, $mon, $day, $hour, $min, $sec, $micro_sec); return $time_stamp; } //////////////////////////////////////////////////////////////////////////////// * 外部プログラム呼出 [#s0f44447] //============================================================================== ** 返り値を得たい場合 [#cef38dbb] $ret = system ("呼び出すコマンド"); - この $ret のように、&color(red){サブプロセスの返り値の場合、''"<< 8"'' された値になっている};ので、正しい値を得るには、以下のようにしてやる必要がある: $ret = $ret >> 8;; //============================================================================== ** 呼び出したコマンドのプロセスに入力値を与えたい場合 [#j48e4f5c] if (!open (PROG, "| 呼び出すコマンド")) { print "open error\n"; return; } print PROG "送りたいデータ"; ・・・・ close (PROG); # 最後の後始末 //============================================================================== ** 呼び出したプログラムの出力を得たい場合 [#m821c904] if (!open (PROG, "呼び出すコマンド |")) { print "open error\n"; return; } my (@out) = <PROG>; # 複数行の出力も全て、配列 @out に格納する。 close (PROG); //////////////////////////////////////////////////////////////////////////////// * File I/O [#l78587ba] //============================================================================== ** ファイルの copy, rename [#x409af09] - ファイルの copy rename には、 File::Copy を使う。 use File::Copy; copy("コピー元のファイル名", "コピー先のファイル名"); # ファイルのcopy move("移動元", "移動先"); # ファイルのrename //------------------------------------------------------------------------------ ** ディレクトリの読み込み [#nc7671b7] - opendir, readdir, closedir を使う。割と普通なやり方。 my ($dir) = './data'; if (!opendir (DIR, "$dir")) { die "Could not open dir: $dir\n"; } my (@read_tmp); while (defined($ent = readdir(DIR))) { if (($ent =~ /^\.$/) || ($ent =~ /^\.\.$/)) { next; } # $read_tmp[$#read_tmp+1] = $ent; # $read_tmp[$#read_tmp+1] = "$dir/$ent"; push (@read_tmp, "$dir/$ent"); } closedir(DIR); # 思った通りの並びで読み取ってくるわけではないので、sort しておいた方がいい場合もある。 my (file_entries) = sort { $a cmp $b } @read_tmp; //============================================================================== ** ファイルテスト [#c2a19583] |~演算子 |~説明| |-r | 読み込み可能| |-w | 書き込み可能| |-x | 実行可能で| |-o | 実行者とファイルの所有者が同一| |-R | 実uid/gidで読み込み可能| |-W | 実uid/gidで書き込み可能| |-X | 実uid/gidで実行可能| |-O | 実uidとファイルの所有者が同一| |-e | ファイルが存在する| |-z | ファイルサイズが 0| |-s | ファイルサイズが 0 以外(大きさを返す)| |-f | ファイルは通常ファイル| |-d | ファイルはディレクトリ| |-l | ファイルはシンボリックリンク| |-p | ファイルは名前付きパイプ| |-S | ファイルはソケット| |-b | ファイルはブロック型の特殊ファイル| |-c | ファイルはキャラクタ型の特殊ファイル| |-t | ファイルハンドルが tty としてオープンされている| |-u | ファイルの setuid ビットがセットされている| |-g | ファイルの setgif ビットがセットされている| |-k | ファイルの sticky ビットがセットされている| |-T | ファイルがテキストファイル| |-B | ファイルがバイナリファイル| |-M | perl起動時における、ファイルの更新時刻からの日数| |-A | perl起動時における、ファイルの参照時刻からの日数| |-C | perl起動時における、ファイルの作成時刻からの日数| - 使用例 if (-e $file_name) { # ファイルが存在した場合の処理を記述 } //============================================================================== ** カレントディレクトリの取得 [#r9809812] - getcwdを使う。 #!/usr/bin/perl use Cwd 'getcwd'; # for getcwd my ($cur_dir_name) = getcwd; # current dirが Full Path で代入される。 # もし、Full Pathではなく現在のディレクトリ名だけが欲しければ、さらに次のようにする。 $cur_dir_name =~ s/.*\///g; //============================================================================== ** カレントディレクトリの変更 [#q5724e3a] - chdir を使う my ($ret) = chdir ($path); -- return 1: 成功 0: 失敗 → $! に失敗原因のメッセージ文字列が格納される - File::chdir を使う use File::chdir; $CWD = ~/work; -- File::chdir を使う場合、環境によっては追加パッケージの導入が必要となる。 --- debian の場合は [[libfile-chdir-perl>https://packages.debian.org/search?lang=ja&searchon=names&keywords=libfile-chdir-perl]] をインストールする。 //============================================================================== ** ファイル属性の取得 [#a1fcdaee] - stat 関数を使う。 @INFO = stat( $file ); |ファイルシステムのデバイス情報 |$INFO[0] | |Iノード番号 |$INFO[1] | |ファイルモード(パーミッション等) |$INFO[2] | |対象ファイルのリンク数 |$INFO[3] | |対象ファイルの所有書のユーザID |$INFO[4] | |対象ファイルのグループのグループID |$INFO[5] | |デバイス識別子 |$INFO[6] | |ファイルサイズ(バイト数) |$INFO[7] | |最終アクセス時間 |$INFO[8] | |最終変更時間 |$INFO[9] | |最終 I ノード変更時間 |$INFO[10] | |最適ブロックサイズ |$INFO[11] | |割当てられているブロック数 |$INFO[12] | //============================================================================== ** Read/Write [#t087d466] //------------------------------------------------------------------------------ *** 簡単なread [#u01bca45] if (!open (FILE_HANDLE, $file_name)) { die "file open error: $file_name\n"; } my (@lines) = <FILE_HANDLE>; # ファイルの全ての行を一気に @lines に読み出し close (FILE_HANDLE); # 読みだしたので、close foreach $line (@lines) { # 処理を記述 } - 長所 -- 全ての行が一つの配列に格納されるため、読み出し後、行単位でランダムアクセスが出来る -- すぐにclose出来る - 短所 -- 読みだすファイルが巨大な場合、メモリ不足に陥り正常に処理終了出来ない場合がある。 //------------------------------------------------------------------------------ *** 大容量ファイルも読み込めるread [#dbb9cd54] if (!open (FILE_HANDLE, $file_name)) { die "file open error: $file_name\n"; } while ($line = <FILE_HANDLE>) { # 1行ずつ読み出して逐次処理する。 # 処理を記述 } close (FILE_HANDLE); # 読みだしたので、close - 長所 -- 巨大なファイルも読み出せる - 短所 -- 先頭からの逐次処理しか出来ない //////////////////////////////////////////////////////////////////////////////// * 文字列処理 [#pf775480] //============================================================================== ** split() : 区切り文字によるフィールド分割 [#k3a2b186] - 書式: @dst = split (/$splitter/, $src); @dst : 区切り文字で分割された各フィールドが配列として格納される先。区切り文字は除去して格納される。 $splitter : Tab区切りの場合はTab(\t) SPACE区切りの場合は SPACE( ) コンマ区切りの場合はコンマ(,) ...といった具合に指定する $src : 区切り文字でフィールドが区切られた文字列。 //============================================================================== ** 文字列を1文字づつ処理する [#ke53ce9f] - split() で分割する $string = "hogefuga"; @chars = split (//, $string); foraech $ch (@chars) { // 1文字づつの処理 } あるいは $string = "hogefuga"; foraech $ch (split (//, $string)) { // 1文字づつの処理 } //============================================================================== ** シングルクォートを含む文字列の処理 [#jcf8f9fc] - シングルクォートは、単にエスケープしただけでは正しく解釈されない為、'''\''' ではなく、それ自体を更にシングルクォートで括って '''\'''' とする必要がある。 -- 例:シングルクォートを削除する $ perl -pe 's/\'//' # 単にシングルクォートをエスケープ :正しく動作しない $ perl -pe 's/'\''//' # エスケープしたシングルクォートをさらにシングルクォートで括る:正しく動作する - 例:シングルクォートを含む文字列を処理する例として、lisp の require 文から、require されるモジュール名の抽出をしてみる。 -- lisp の require 文は以下の書式になっている: (require '<モジュール名>) -- シングルクォート以前と、最後の小括弧閉じを削除する $ echo "(require 'magit)" | perl -pe 's/.*'\''//' | perl -pe 's/\)//' magit //////////////////////////////////////////////////////////////////////////////// * 呼び出し元関数名を取得する [#bc8c6672] - C/C++ の __func__ マクロの代用機能(perlには同様のマクロはない) - その代わり、「呼び出し元関数の名前」を取得する機能があって、これを関数化して呼び出すと、 __func__ マクロ同様の使い方が出来る。 sub get_func_name{ # 呼び出し元の関数名を取得 my ($parent_func_name) = (caller 1)[3]; $parent_func_name =~ s/.*:://; return $parent_func_name; } //////////////////////////////////////////////////////////////////////////////// * コメント [#dcfabb6f] - 行単位のコメントは、半角 "#" print "Hello World.\n"; # これより右側はコメント - 複数行をコメントアウトする -- 開始記号: =pod -- 終了記号: =cut =pod ここは、コメントになる =cut print "Hello World.\n"; -- &color(red){このコメントアウト記号は、行頭に記述しないと正しくコメント開始・終了記号として認識されない場合があるので注意!}; - 複数行をコメントアウトする(Acme::Comment) -- Acme::Comment を使用すれば、 C/C++/Java 風のコメントアウトが使用出来る。 Acme::Comment; /* ここは、コメントになる。 */ -- CGI で使用する場合には注意が必要(サーバによっては使えない場合がある)。 //////////////////////////////////////////////////////////////////////////////// * pack / unpack [#w075669c] ** pack [#qee4c16f] - [[pack>https://perldoc.jp/func/pack]]