2015年 10月 06日

拡張子のないファイルに一括で拡張子を付与する

rename コマンドで可能だぞ。rename コマンドは Perl の式でファイル名を置換可能だ!

「拡張子のないファイル」にマッチするシェルのglob表現は簡単ではないし、シェルスクリプトの for 文の文法はだいたいいつも忘れている。rename コマンドは Perl の正規表現という大変慣れたものを使え、sed や awk などと比べても安心感がある。

rename -v -n 's/^([^.]+)$/$1.txt/' *

置換が行われなかった場合リネームは行われない。-n はドライランなので実際に実行するときは外す。-v は verbose オプションで、つけておいたほうがいい。

この正規表現では . を含まないファイル名にマッチさせ、.txt をファイル名にあとに追加している。

備考

Ubuntu の wiki によると

UNIXにこのコマンドがないこともあって余り知られていませんが、どのLinuxにも含まれています。

https://wiki.ubuntulinux.jp/UbuntuTips/FileHandling/RenameCommand

らしいが、本当のところどうなのかよくわからないぞ! すくなくとも Ubuntu では使用可能だ! なお OS X には含まれていない。

OS X では brew install rename で入るが、これは Ubuntu のパッケージのものとはまた別のバージョンである。ただし高機能版であり、-v -n オプションは互換性がある (-n の longname は Ubuntu 版では no-act だが、homebrew で入るものは --dry-run または --just-print となっている)。

copyright

ライセンスはどちらも This script is free software; you can redistribute it and/or modify it under the same terms as Perl itself. となっており Perl と同一 (Artistic License) のようだ。原版と同じ名称を使ってはいけないライセンスだった気がするが……

Ubuntu 版

# This script was developed by Robin Barker (Robin.Barker@npl.co.uk),
# from Larry Wall's original script eg/rename from the perl source.

homebrew 版

AUTHORS
Aristotle Pagaltzis

Idea, inspiration and original code from Larry Wall and Robin Barker.

となっており、Larry Wall (Original?) → Robin Barker (Ubuntu版) → Aristotle Pagaltzis (homebrew版) という感じで進化している雰囲気がある

2014年 09月 30日

TheSchwartz の prioritize を有効にしたときの挙動は愉快

ワーカー使っていると、一部のジョブだけちょっと優先的に実行したいということがあるので、TheSchwartz の場合、prioritize => 1 にしてプライオリティを有効にするとよいのですが、挙動でハマったのでメモをしておきます。

今回のケース

そもそもジョブごとにプライオリティはつけていないが、一部のジョブだけ手動対応で優先して処理を行いたくなった。

やったこと

該当ジョブを

update job set run_after = insert_time, grabbed_until = insert_time, priority = 1 where ...;

で優先順位を設定しすぐに実行開始できるようにした

結果

_人人人人人人人人人人人人_
> なかなか実行されない <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

というのも、TheSchwartz は priority 順でソートして50件取得して、それをシャッフルしてから1件選んで実行する、という挙動をするため。

つまり、優先順位を設定したとしても有象無象と混ぜられた 1/50 のクジに当たらなければ、そのジョブは実行されない。1/50 って2%だよ…… 2%…… 100回ジョブ実行しようとしてようやく86.7%の確率だよ……


どうするか?

とにかく入ってるジョブをすぐ実行したい場合

  • $TheSchwartz::FIND_JOB_BATCH_SIZE (デフォルト50) を 1 にする
  • 該当ジョブ以外の run_after を適当に未来に設定する
  • ジョブIDを指定して1回だけワークするスクリプト書いて手動実行

ぐらいしかない。

2014年 02月 17日

Locale::Maketext::Extract でスキャンだけする。

Locale::Maketext::Extract::Plugin 以下にはいろいろ対応してるフォーマットがあったりする。まぁ大抵一緒なので頑張って使う必要もないけど、これらを使って .po ファイルを作らないまでも、msgid の抽出だけ行いたいという場合があったりします。そんなときは直接 LME インスタンスを作って extract_file をかけて compile すれば、とれるようになるみたいです。

use Locale::Maketext::Extract;
use Locale::Maketext::Extract::Plugin::Xslate;
use File::Zglob;

my $lme = Locale::Maketext::Extract->new(
    plugins => {
        perl => [ 'pm' ],
        xslate  => {
            syntax     => 'TTerse',
            extensions => [qw/ tt /],
        },
        generic => [ 'js' ]
    },
    warnings => 1,
    verbose => 0,
);
 
 
for (zglob('lib/**/*.pm'), zglob('template/**/*.tt'), zglob('static/**/*.js')) {
    $lme->extract_file($_);
}

$lme->compile(1); # これをしないと msgids がとれない

for my $msgid ($lme->msgids) {
    say $msgid;
    $lme->msg_positions($msgid); # 見つかった場所がとれる
}
2013年 04月 12日

CSRF 防止用トークンの自動チェックの問題と解決

自分が作るウェブアプリケーションでは基本的に以下のような規則を守るようにしている

  • GET だけで副作用 (DB書きこみなど) を伴う処理をしないこと
  • POST 時には自動的に CSRF 防止用トークンをチェックすること

これだけで単純な CSRF はまず防げくことができるからだ (実際は CSRF 防止用トークンの生成方法が多少問題になるが、作るウェブアプリケーションによって、どれほど厳密にすべきかが違うため、そのたび考えてやりかたを変えてる)

このような設計をした場合、全ての POST するフォームに input type="hidden" とかで CSRF 防止用トークンを埋めこむ必要がある。ウェブアプリケーションフレームワークによっては、防止用トークンを埋めこむところも自動的にやってしまうものもあるが、外部サーバへ POST するフォームなんかを作ったとき (ASP型の外部サービスとか)、意図せずトークンが漏れるのがちょっと不安なので自分は手動で全部入れることにしている。たとえ入れ忘れても、普通にチェックしていれば必ず気付くし、一行コピペするだけなのでこれで問題はまず起きない。

しかし一方、外部のアプリケーションから直接 POST されるような場合 (なんらかのコールバックとか)、当然ながら CSRF 防止用トークンは送られてこないため、自動チェックにはねられてしまう。なので、こういう場合は明示的に自動チェックをオフにする必要がでてくる。これが地味にややこしい。

Perl で Router::Simple などを使って適当に本体をディスパッチし、その前 (before_dispatch 的なもの) で自動チェックをかけるアプリケーションがあったとする (シンプルに作るとそうなるだろうというもの)。そんなときなんとなく以下のように書ければいいなと一瞬思ってしまうが、これはうまく行かない。

sub dispatch_foo {
    my ($class, $c) = @_;
    $c->check_csrf(0);
    # do something...
}

自動チェックはこのメソッドの前に実行されており、この段階、すなわち呼ばれたあとに「チェックしない」ことを宣言しても遅い。自動チェックをやめて、CSRF チェックするところにだけ $c->check_csrf(1) とか書いてチェックを明示的にするようにする、という解決方法もあるだろうけど、危ういので避けたい。

とにかく、自動的にチェックを行っている以上、そのチェックが行われる前に「この URI (やサブルーチン) に対しては CSRF チェックをしない」宣言をしておいて、自動チェック時にそのフラグを見る必要がある。理想としてはコードのコンパイル時に行われるのが望ましい。例えば以下のように

sub dispatch_foo {
    my ($class, $c) = @_;
    # do something...
}
__PACKAGE__->check_csrf(dispatch_foo => 0);

この場合、サブルーチンに対して CSRF チェックを自動チェックするかどうかを check_csrf() クラスメソッドで宣言しておくイメージになる。

これでとりあえずいいとは思うけれど __PACKAGE__ とか書きにくいし、dispatch_foo は2度出てくるしで、なんとかしくなる。

結論を言うと、この場合、以下のように CODE Attribute を使うのが一番スッキリいくように思った。

sub dispatch_foo : check_csrf(0) {
    my ($class, $c) = @_;
    # do something...
}

Catalyst 以来、最近は CODE Attribute を使っているものを殆ど見ておらず忘れていたのだけれど、こういうときはやはり便利だな、と思った。

これに従って、自分でウェブアプリケーションを作るときのスケルトンにも同じような仕組みを入れた。

2006年 07月 07日

Perl 雑多メモ

  • ファイル処理には IO::File を使え。(IO::Handle, IO::Seekable)
  • LOCK_EX とかは use Fcntl;。でも IO::File::Lockable っていうモジュールがあったりする (使う?)
  • EOF まで一気に読み込む (ruby の File#read) には join "", $f->getlines とか、リストコンテキストで I/O 演算子使って join?
  • package; は deprecated. 戻すときは明示するらしい package main;
  • @ISA (あいえすえー、と読んでた) って何だよって思ってたけど、is_a だった。

クラスの作り方を寝ると忘れる。

  • 継承のやりかたを覚える。
  • use の挙動を把握する (use すると import ルーチン呼ぶんだぜって教えてもらった)
2006年 05月 18日

今日のぺるる コンテキスト

コンテキストについて学んだよ。今回もとぴあさんくらむさんに親切に教えてもらったよ。

Perl ではコンテキストっていうのがあって、意識していないとハマるらしい (というかハマった)。サブルーチンを呼ぶところだけを見ても、そのサブルーチンが何を返すかがわからない。

$ perlsh
main[1]$ @arr = localtime;
56
21
1
18
4
106
4
137
0
main[2]$ $scr = localtime;
Thu May 18 01:22:02 2006

最初、なんでただの配列がスカラ変換のときに、タイムオブジェクト?のように見なされて時刻文字列になるんだ?って思ったんだけど、これは localtime がコンテキストによって挙動を変えた (!) 結果らしい。localtime っていう部分だけを見ても、localtime が何を返すかはわからない。


そのコンテキストとやらには、大きくわけてリストコンテキスト・スカラコンテキスト・void コンテキストがあるようだ。

sub context {
if (defined wantarray) { # void context では undef
if (wantarray) { # list context では true
print "List Context!\n";
} else {
print "Scalar Context!\n";
}
} else {
print "void Context!\n";
}
return;
}
@arr = context;
$scr = context;
context;
__END__
List Context!
Scalar Context!
void Context!

という感じで、サブルーチン内で wantarray を呼ぶことによりコンテキストの分岐ができる。wantarray っていう名前はおかしい (配列とリストは区別がある) みたいだけど、歴史的事情がきっとあるんだろう? perldoc でも wantlist であるべきだった的なことが書いてある。

たぶんコンテキストの理解は Perl では必須だろうけど、こういうのって他の言語だとあんまり見ないよね、っていう感じだった。


他今日知ったこと

  • use vars は obsolete。変わりに our
  • 擬似ハッシュには触るな (obsolete)
2006年 05月 11日

今日のぺるる - grep, リファレンス

grep とリファレンスがわからなかった。

ちなみに一通りドキュメントは読んでみて、概念っぽいのはわかっているんだけど、実際の挙動がわからない、という感じ。ようするにだめだめ。とぴあさんに助けてもらいつつちょっとわかってきた。

[{:foo=>:a, :bar=>:b}, {:foo=>:c, :bar=>:d}].find {|i| i[:foo] == :a}[:bar]

っていう Ruby のコードを Perl で書きたい場合。

# 答え
(grep { $_->{foo} eq "a"} [{foo=>"a", bar=>"b"}, {foo=>"c", bar=>"d"}])[0]->{bar};
#以下間違い
# クロージャのあとにカンマは要らない
grep { .. }, [..];
# -> 演算子によって左辺がスカラ文字列に変換されてしまうらしい
# この場合 grep でヒットした配列の数の文字列に対してハッシュ操作を
# 行おうとするので、エラー
(grep { .. } [])->{bar};

リファレンスを実データに戻す文法がわからなかった。

$a = [1, 2, 3]; #=> $a is reference of array
@{$a}; #=> で array に戻る
# @($a) と書いて構文エラー

それと Hash ref, Array ref から直接アロー演算子使って中身を取り出すときの構文

$a = ["a", "b", "c"];
$a->[1]; #=> "b"
$h = {0 => "a", 1 => "b", 2 => "c"};
$h->{1}; #=> "b"

一番なるほど!て思ったのは $_

grep { $_ eq "a" } ("a", "b", "c");
foreach (@{$a}) {
print $_;
}

$_ ってよく見るけどなんやねんって思ってた。

grep {|$_| ..}

っていう Ruby 風の説明をされてよくわかった。


疑問

foreach (@{$a}) {
foreach (@{$b}) {
$_;
}
$_; #=> スコープは?
}

あとで実行してみよう。

foreach $i (@{$a}) {
foreach $j (@{$b}) {
print "$i, $j";
}
}

みたいにかけるのかな。my をつけるべき? (つけられる?) っていうか for と foreach は同じなのね

コメントでの指摘で修正

今日のぺるる $_ の続き。

my @a = ("a", "b", "c");
for (@a) {
for (@a) {
print $_;
}
print "\n  $_\n";
}
__END__
abc
a
abc
b
abc
c

ちゃんとブロックローカル (?) だった。

2005年 07月 10日

Perl たんのツンデレの件

Perl で何書けばいいんですがって感じでもうしょっぱなからダメダメです。オープンマインドな Ruby たんが優しすぎるのです。もう黄身無しでは(きもい