

安物のネットワークカメラを買いました。ftp経由で転送されてくるのですが、  
設定範囲が少なくて、妙案を探しています。(dbpower c300e、アマゾンで賛否両論あり)

/2016-11-08/Capture/13_57_20_200.jpg  
/2016-11-08/Capture/13_59_20_319.jpg  
/2016-11-08/Capture/14_02_20_498.jpg

上述のような要領で転送されてくるのですが、httpdで公開したいので、以下のような、  
いつでも同じパスでアクセスできるようにしたいです。サーバ側はfreebsd、  
tcsh等でして、crontabも使えるものの、スクリプトの書き方が分かりません。

/cam1.jpg

findで最新日時のjpgを探し、mvで公開ディレクトリへ名前指定でコピーしたあと、  
カメラから転送されて、無限に増え続けるファイルを片付けるため、  
日付付きのディレクトリを一括削除といった流れになるかなと  
思うのですが、camera.shのようなスクリプトにまとめる際、  
中身はどんなものが良いでしょうか?

find /home/camera-user/ -type f -name " _.jpg" | xargs -J % mv ...  
mv -f /home/camera-user/_

xargsでつなげて、freebsdでは、jオプションがあるのはわかったものの、  
mvの書き方がはっきりしません。日付の新しいものをsortするには、  
さらにパイプする必要がありそうです。ヒット順は、最後が最新ファイルに  
なるので、最後1ファイルを移動するだけでも良いような・・・。

mv % /home/www/public_html/cam1.jpg


CC BY-SA 3.0
2016-11-08T06:18:23.113
30204
2016-11-09T17:41:35.123
19218
question
shellscript freebsd
サーバ上の特定ディレクトリ以下の最新1ファイルをコピーするには?


ファイルの最終修正日時の新しい順に取得し head で先頭1行取得したものを xargs でmv

こんな感じでどうでしょうか？  
(複数ディレクトリからの取得にしてみました)

freebsd

[code]

    ls -1 -t /home/camera-user/*/Capture/*.jpg | head -n 1 | xargs -J % mv -f % /destdir/dest.jpg
    
[/code]

linux

[code]

    ls -1 -t /home/camera-user/*/Capture/*.jpg | head -n 1 | xargs -I {} mv {} /destdir/dest.jpg
    
[/code]

linux(bash)の方しか動確してません


CC BY-SA 3.0
2016-11-08T07:17:54.793
30207
2016-11-08T08:49:54.550
2016-11-08T08:49:54.550
7343
7343
30204
answer


最新日付のディレクトリに cd で移動して、Capture の下の最新ファイルをコピーすれば良いと思います。  
Cシェル書けないので 以下は sh で書きました。freebsdがないので試してません。間違っていたらすみません。

[code]

    #!/bin/sh
    PATH=/bin:/usr/bin
    export PATH
    
    # 最新の日付ディレクトリ
    cd /
    DIRNAME="`ls -1d ????-??-?? | sort | tail -1`"
    
    # 最新ファイル
    cd  $DIRNAME/Capture/
    FILE="`ls -tr1 *.jpg | tail -1`"
    
    # ファイルコピー
    cp $FILE /path/to/www/cam1.jpg
    
    # 日付ディレクトリを一括削除
    cd /
    rm -rf ./????-??-??
    
[/code]


CC BY-SA 3.0
2016-11-08T09:43:10.320
30210
2016-11-08T09:43:10.320
5008
30204
answer


こんな感じかなぁ? 同一ファイルシステムでないと `mv` で発生するコピーが無駄かも。

[code]

    #!/bin/sh
    
    set -u
    
    incoming_dir="/var/ftp/camera"
    latest_file="/var/www/html/camera/latest.jpg"
    
    export latest_file
    
    find "$incoming_dir" \
      -type f \
      -name "*.jpg" \
      -exec sh -c '
        set -u
        for incoming_file in "$@"; do
          if [ "$incoming_file" -nt "$latest_file" ] || [ ! -f "$latest_file" ]; then
            mv -- "$incoming_file" "$latest_file"
          else
            rm -- "$incoming_file"
          fi
        done
        ' sh {} + \
    ;
    
    find "$incoming_dir" -type d -empty -delete
    
[/code]

テストしたい場合は `mv` と `rm` の前に `echo` でも追加してみてください。


CC BY-SA 3.0
2016-11-08T17:44:00.670
30253
2016-11-08T18:17:18.310
2016-11-08T18:17:18.310
3061
3061
30204
answer


`mv`せずにApacheにURLのルーティングを任せてしまうのはどうですか？

ファイルが多くなると`find`はコストが高そうですし、  
FTPにアップロードされるファイルパスは、幸い単純な比較だけで  
最新のファイルを特定できそうなので、ディレクトリツリーを歩く小さなスクリプトとmod_rewriteとRewriteMapを利用します。

OSX El capitan
で動作確認済み。perlスクリプトをApacheにサーブさせないようにするにはもう一手間ぐらい必要そうですが、隠すほどのものでもないかも。

これならcronを待たなくてもいいですし。

### httpd.conf

[code]

    RewriteMap latest "prg:/var/www/find_last_filepath.pl"
    
[/code]

### /var/www/.htaccess

[code]

    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule "^latest.jpg$" "${latest:pict}" [R=302,L]
    
[/code]

### /var/www/pict/* (FTPで直接ここにアップロードさせる)

[code]

    /var/www/pict/2016-11-08/Capture/13_57_20_200.jpg
    /var/www/pict/2016-11-08/Capture/13_59_20_319.jpg
    /var/www/pict/2016-11-08/Capture/14_02_20_498.jpg
    
[/code]

### /var/www/find_last_filepath.pl

[code]

    #!/usr/bin/perl
    use strict;
    use warnings;
    use FindBin qw($RealBin);
    use File::Spec::Functions qw(catfile canonpath);
    
    sub find_last {
        my @path = @_;
    
        # @pathにあるファイルを読む
        my @ls = reverse sort { $a cmp $b } grep {/^[^.]/} do {
            my $path = catfile(@path);
            opendir my $dir, $path or die "$!: $path\n";
            readdir $dir;
        };
    
        # ファイルとディレクトリに分割
        my @files = grep { -f catfile(@path, $_) } @ls;
        my @dirs  = grep { -d catfile(@path, $_) } @ls;
    
        # もしファイルもディレクトリも無ければこのディレクトリを諦める
        return if @files == 0 && @dirs == 0;
    
        # ディレクトリだけなら順に探索
        unless (@files) {
            for my $dir (@dirs) {
                my @found = find_last(@path, $dir);
                return @found if @found;
            }
    
            # どのディレクトリにも見つからなければやっぱり諦める
            return;
        }
    
        # ファイルだけなら最後のファイルを返す
        unless (@dirs) {
            return (@path, $files[0]);
        }
    
        # ディレクトリ名がファイル名より後ならディレクトリを探索しつつ
        if (($files[0] cmp $dirs[0]) < 0) {
            my @found = find_last(@path, $dirs[0]);
            return @found if @found;
        }
    
        # ファイル名にフォールバック
        return (@path, $files[0]);
    }
    
    
    # STDINに探索の開始パスを与える。
    local $| = 1;
    chdir $RealBin;
    while (<STDIN>) {
        chomp;
        open my $fh, '>', '/tmp/prglog';
        printf {$fh} "%s\n", $_;
        my @found = find_last($_);
        printf "%s\n", canonpath(catfile(@found)) if @found;
    }
    
    1;
    
[/code]


CC BY-SA 3.0
2016-11-09T17:41:35.123
30286
2016-11-09T17:41:35.123
62
30204
answer