woshidan's loose leaf

ぼんやり勉強しています

いい加減わからない感じが強いのでsedについて少し調べてみた

sedとは

sedstream editor の略で、主にファイルかファイルの指定がなければ標準入出力を読んで何かしたら入力を修正したら標準出力へ書き込んでくれる、といった使い方が主なスクリプト言語です。

sedの記法

コマンドを一つだけ入力する場合とそうでない場合で記法が異なります。

     sed [-Ealn] command [file ...] #
     sed [-Ealn] [-e command] [-f command_file] [-i extension] [file ...]

sedのコマンドの種類

コマンドには、 http://itpro.nikkeibp.co.jp/article/COLUMN/20060227/230879/ で紹介されているものから

  • a 文字列 ... 文字列を追加する。ただし改行をしたい場合はその前に\を付ける
  • i 文字列 ... 文字列を挿入する。ただし改行をしたい場合はその前に\を付ける
  • r ファイル名 ... 指定したファイルを読み出し,追加する
  • d ... パターン・スペースを削除する
  • s/置換条件/置換文字/ ... 置換条件を置換文字に変換する。最後にgを付けた場合は置換条件に当てはまるすべての文字列が置換される
  • p ... 行を出力する

などがあります。a コマンドなどはMac OS環境下だとうまく動かないことがあるので、その場合はこちらのページを参考に gnu-sed を入れるとよいようです。

$ echo "test" | sed s/t/gu/
guest
$ echo "test" | sed p
test
test

// test.csv
// cat,dog,cow
// horse,wolf,giraffe
// anteater,bear,ape

$ sed 1d test.csv
horse,wolf,giraffe
anteater,bear,ape

今回は主に s コマンドの話をしたいので先に進みます。

アドレスとは

各コマンドについて、アドレス、コマンド1文字、コマンドパラメータなどを指定しますが、アドレスとは、処理を行う範囲の指定のことだそうです。指定方法について、 https://qiita.com/mattintosh4/items/4e4d44016be15333af11 の記事より引用させていただくと、

アドレス 範囲 行番号指定 パターン指定
0アドレス 全ての行 何も書かない 何も書かない
1アドレス 指定行 1、や 2、$ など /^1行目/ など
2アドレス 指定行〜指定行 1,3、2,$ など /^1行目/,/^3行目/ など

という風になっています。わかりづらいので、

cat,dog,cow
horse,wolf,giraffe
anteater,bear,ape

のようなCSVファイルを処理して確認してみましょう。

// 0アドレス(何も記述していない) => すべての行で処理される
$ sed s/a/A/g test.csv
cAt,dog,cow
horse,wolf,girAffe
AnteAter,beAr,Ape
// 1アドレス(数字や記号を一つだけ書く) => その行だけ処理される
$ sed 3s/a/A/g test.csv
cat,dog,cow
horse,wolf,giraffe
AnteAter,beAr,Ape
// 2アドレス(コンマ区切りで二つ指定する) => 指定した複数行が処理される
$ sed 2,3s/a/A/g test.csv
cat,dog,cow
horse,wolf,girAffe
AnteAter,beAr,Ape

sedのsコマンドでできること

眠たいのでバーッと書いちゃいます。

// 置換文字の中でマッチしたキーワードを利用する
$ echo "test" | sed s/t/gu/
guest

// s/置換条件/置換文字/ の置換文字の部分に&を置くと、マッチした単語の部分が出力される
$ echo "test" | sed 's/t/gu &/'
gu test
// 3行目の内容 anteater,bear,ape
$ sed -n '3s/a/ Changed &/gp' test.csv
 Changed ante Changed ater,be Changed ar, Changed ape

// s/置換条件/置換文字/ で置換条件に()を使ってグループ化し、置換文字の部分に\nを置くと、n番目に()のグループでマッチした部分が出力される
$ echo "test" | sed 's/\(te\)\(st\)/\2  \1/'

// s/置換条件/置換文字/ の置換条件に正規表現が使える
// 行頭 ^, 行末 $ など
$ sed -n 's/\([bcd]\)/\1_/gp' test.csv
c_at,d_og,c_ow
anteater,b_ear,ape

// -e コマンドで sコマンドをつないで、複数の条件で置換ができる
$ sed -e 's/\([bcd]\)/B/g' -e 's/\([efg]\)/E/g' test.csv
Bat,BoE,Bow
horsE,wolE,EiraEEE
antEatEr,BEar,apE

// s/置換条件/置換文字/ の後ろの部分にgをつけるとマッチした部分全体が、pをつけるとその部分を追加で出力になる
$ sed 3s/a/A/gp test.csv
cat,dog,cow
horse,wolf,giraffe
AnteAter,beAr,Ape
AnteAter,beAr,Ape

// s/置換条件/置換文字/ の後ろの部分にgをつけるとマッチした部分全体が、pをつけるとその部分を追加で出力になる
$ sed 3s/a/A/gp test.csv
cat,dog,cow
horse,wolf,giraffe
AnteAter,beAr,Ape
AnteAter,beAr,Ape

// s/置換条件/置換文字/p を -nオプション(対象の行だけ出力)と合わせると、置換処理した部分だけ出力できる
$ sed -n 3s/a/A/gp test.csv
AnteAter,beAr,Ape

他にも色々ありますが、 https://qiita.com/hirohiro77/items/7fe2f68781c41777e507 などを見ることにして慣れてきたので今日はここまでにします。

参考

利用条件が緩いzlibライセンスについて

ライブラリのライセンス表記についてはアプリやライブラリを開発していると悩ましいところで、使ってはいけなさそうなLPGL系を避けながら雑にすべてのライブラリのライセンス表示ページを作ったりして誤魔化しがちです。

しかし、中には著作者の偽装はしてはいけないものの、ライセンス表記を要求されないものもあるようです。

その一つがzlibライセンスです。Wikipediaから条項の概要を引用すると、

- ソフトウェアはas-is(現状のまま)ベースで利用される。著作者は利用により起こりうる損害に対する責任を負わない。
- 商用利用を含め、ソフトウエアの改変、配布は以下の制限の下で許可される:
  - 1. このソフトウェアの原著作者であると詐称してはならない(ただしその表示義務は無い)。
  - 2. ソースコードを改変した場合はオリジナルのままだと勘違いされないようにしなければならない。
  - 3. このライセンス表示をソースコード配布物から削除してはならない。

ライセンスは、バイナリコードが配布されているならば、利用可能なソースコードを必要としない。

となっています。もう少し自分に近い言葉でまとめなおすと

  • ソースコードの状態でライブラリを自身のコードに含む場合はこのライセンス文をソースコードから削除してはいけない
  • 改変した場合は改変したことがわかるようにする
  • ライブラリに変更がない状態で静的リンクで利用するならば、ライセンス表記は不要

となります。一つ一つ具体的に確認しないと怖いですね、現場からは以上です。

参考

S3に入っているファイルの状況を調べるのにCloudWatchが便利、という話

S3に限った話ではないのですが、ちょっと追加で調べたいなというときにメトリクスやその間隔を調整できて便利でした。まる。

S3は上部メニューの分析から各バケットのメトリクスを確認できるのですが、このメトリクス表示する期間がかなり限定された相対期間のみなのが、メトリクスの数値を見積もりに使いたいのでちょっと例外的なイベントがあった日を外したいなどの場合にちょっと不便です。

f:id:woshidan:20170923120743p:plain

この場合、上図で囲まれた部分をクリックすると同じメトリクスを CloudWatchで確認することができるようになります。

f:id:woshidan:20170923121015p:plain

CloudWatchでは、日付をカスタムで変更することが可能です。

f:id:woshidan:20170923121739p:plain f:id:woshidan:20170923121742p:plain

また、ログファイルのバケットの場合、CloudWatch Logsでログのフィルタリングを設定してアラートを出したり、といった設定も可能です。

現場からは以上です。

配列の中のハッシュの要素で検索したい

cross joinUNNEST を利用する。

UNNEST はSQLの中では UNNEST 関数は、指定された配列の各エレメントにつき 1 行が含まれる結果表を戻す ものだそうです。

前に書いた Athenaの記事 で少し調べたのですが、覚えられてなかったので上記を踏まえてもう一回。

{
    "animals": [
                {"name": "ぽち", "kind": "dog"},
                {"name": "たま", "kind": "cat"}
            ]
}
CREATE EXTERNAL TABLE IF NOT EXISTS animal_logs (
  animals array<struct<name:string, kind:string>>
  )           
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://exmaple-woshidan-test/athena_nest_sample';
SELECT * FROM mydatabase."animal_logs" limit 10;
animals
1 [{name=ぽち, kind=dog}, {name=たま, kind=cat}]
SELECT * FROM mydatabase."animal_logs" cross join UNNEST (animals) AS t (animal);
animals animal
1 [{name=ぽち, kind=dog}, {name=たま, kind=cat}] {name=ぽち, kind=dog}
2 [{name=ぽち, kind=dog}, {name=たま, kind=cat}] {name=たま, kind=cat}
animals
1 [{name=ぽち, kind=dog}, {name=たま, kind=cat}]

の表と UNNSET で作成される

animal
1 {name=ぽち, kind=dog}
2 {name=たま, kind=cat}

の表を掛け合わせているので、元のテーブルの一行 x UNNESTしたanimalsの中の行ある結果の表が返ってくる。

これで、 UNNEST した列の配列の各要素へアクセス可能になるので、これを利用して、

SELECT * FROM mydatabase."animal_logs" cross join UNNEST (animals) AS t (animal) WHERE animal.kind='dog';
animals animal
1 [{name=ぽち, kind=dog}, {name=たま, kind=cat}] {name=ぽち, kind=dog}

のように絞り込むことができる。

参考

セマフォとロックとPauseからの復帰

  • セマフォは獲得してから解放するまでのコードを一つのスレッドしか通ってくれるなよ、的なもの
    • 1つ、というのはコンストラクタの引数1から
      • 同時に実行していい数が指定できるロック的な
    • と現状理解している
  • サンプル見てるとこのセマフォでCamera2 APIの利用のタイミングの同期を取っているっぽい
  • 獲得 ~ 解放までの流れ 1
    • CameraManager.openCameraCameraDevice インスタンスを経由して Camera2 APIにアクセスしようとする*1
    • CameraDevice.StateCallbackonOpened, onDisconnected, onError で解放
      • openCamera でCamera2 API へのアクセスを試すあたりの処理に他のスレッドが入ってくるのを制限している
  • 獲得 ~ 解放までの流れ 2
    • CameraCaptureSession, CameraDevice, 他のSurface をcloseする前に獲得
    • 上記close後 or closeに失敗した後に解放
  • ちなみに昨日の自分のコードはそもそも onPauseCameraDevice をcloseしていなかったのでした…

獲得 ~ 解放までの流れ 1

https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java#L268-L271

    /**
     * A {@link Semaphore} to prevent the app from exiting before closing the camera.
     */
    private Semaphore mCameraOpenCloseLock = new Semaphore(1);

https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java#L599-L621

    /**
     * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}.
     */
    private void openCamera(int width, int height) {
        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            requestCameraPermission();
            return;
        }
        setUpCameraOutputs(width, height);
        configureTransform(width, height);
        Activity activity = getActivity();
        CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("Time out waiting to lock camera opening.");
            }
            manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
        }
    }

https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java#L187-L215

    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            // This method is called when the camera is opened.  We start camera preview here.
            mCameraOpenCloseLock.release();
            mCameraDevice = cameraDevice;
            createCameraPreviewSession();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            mCameraOpenCloseLock.release();
            cameraDevice.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            mCameraOpenCloseLock.release();
            cameraDevice.close();
            mCameraDevice = null;
            Activity activity = getActivity();
            if (null != activity) {
                activity.finish();
            }
        }

    };

獲得 ~ 解放までの流れ 2

https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java#L624-L647

    /**
     * Closes the current {@link CameraDevice}.
     */
    private void closeCamera() {
        try {
            mCameraOpenCloseLock.acquire();
            if (null != mCaptureSession) {
                mCaptureSession.close();
                mCaptureSession = null;
            }
            if (null != mCameraDevice) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
            if (null != mImageReader) {
                mImageReader.close();
                mImageReader = null;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
        } finally {
            mCameraOpenCloseLock.release();
        }
    }

回転とかでうまく表示されなくなるの、デバイスとかのせいでないことがわかったから回転再チャレンジしてもいいかも

  • [既存課題]縦向きの場合と横向きの場合でちょうどいいrotationのMatrixを見つける 関連: http://mslgt.hatenablog.com/entry/2015/05/12/013013#rotatescreen 横向き or 縦向きだけの対応で十分では、となり、横向き固定の場合の行列見つけて安堵中
  • [既存課題]縦向きの場合と横向きの場合でそれぞれちょうどよくCameraのPreviewが表示されるようにする
  • [新しい課題]作成した画像のファイルへの書き込み、あるいはImageWriter
  • [新しい課題]ImageReaderでできること他に
  • [新しい課題]TextureViewのカスタムクラス読みに行ってはどうか

*1:という言い方がエラーメッセージ見てるかぎり良さそうなんだが、書いてるノリとしてコールバックを経由してCameraDeviceのインスタンスを取得とか、カメラ起動とかそれくらいのノリ

回転が難しいというか、3つくらいこけてるとどこでこけてるかわからんね 2

  • 純正のカメラアプリでもPORTRAITだけ対応だったりするので、回転してすぐカメラのプレビューが取れないとかはまあある話なのでは
    • 単一方向で対応してその先はその後考えようか
  • バッファとかの回転のマトリックスは固定値返したりとかあるみたいですね
  • rotateとかtranslateの中心座標指定めっちゃ便利
  • pauseからの復帰で戻ってくると CameraDevice.StateCallback のonErrorでエラーコード 1 = (ERROR_CAMERA_IN_USE) を受け取る
    • このエラーは優先的にCamera APIを利用してるクライアントがありますよ、と言う意味だが、もちろんいまテスト用に書いてるものしか利用してないので、そのクライアントは自分のこととなる
      • ロック、ロックお前なのか…
    • 明日はあのサンプルの仰々しいロックの導入を試して見るかなー

Matrix 中心座標を入れられるの便利

    private Matrix getDirectionAdjustedMatrix() {
        //  Surface.ROTATION_90 == landscape
        Matrix matrix = new Matrix();
        int viewCenterX = (int) (mTextureView.getX() + mTextureView.getWidth() / 2);
        int viewCenterY = (int) (mTextureView.getY() + mTextureView.getHeight() / 2);

        matrix.postRotate(270 /* sensorOrientation などとの兼ね合いから決まる */, viewCenterX, viewCenterY);
        matrix.postScale((1.0f * IMAGE_WIDTH / IMAGE_HEIGHT) * (mTextureView.getWidth() / mTextureView.getHeight()), (1.0f * IMAGE_HEIGHT / IMAGE_WIDTH) * (mTextureView.getWidth() / mTextureView.getHeight()),
                viewCenterX,  viewCenterY);

        return matrix;
    }

onPause -> onResumeの復帰がうまくいかない

    @Override
    public void onPause() {
        if (mCaptureSession != null) {
            mCaptureSession.close();
            mCaptureSession = null;
        }
        if (mImageReader != null) {
            mImageReader.close();
            mImageReader = null;
        }
        super.onPause();
    }

    @Override
    public void onResume() {
        super.onResume();

        // When the screen is turned off and turned back on, the SurfaceTexture is already
        // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
        // a camera and start preview from here (otherwise, we wait until the surface is ready in
        // the SurfaceTextureListener).
        if (mTextureView.isAvailable()) {
            openCamera();
        } else {
            mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
                @Override
                public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                    // 先ほどのカメラを開く部分をメソッド化した
                    openCamera();
                }

                @Override
                public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

                }

                @Override
                public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                    return true;
                }

                @Override
                public void onSurfaceTextureUpdated(SurfaceTexture surface) {

                }

            });
        }
    }
    public static abstract class StateCallback {
       /**
         * An error code that can be reported by {@link #onError}
         * indicating that the camera device is in use already.
         *
         * <p>
         * This error can be produced when opening the camera fails due to the camera
        *  being used by a higher-priority camera API client.
         * </p>
         *
         * @see #onError
         */
        public static final int ERROR_CAMERA_IN_USE = 1;
  • [既存課題]縦向きの場合と横向きの場合でちょうどいいrotationのMatrixを見つける
  • [休止][既存課題]縦向きの場合と横向きの場合でそれぞれちょうどよくCameraのPreviewが表示されるようにする
  • [新しい課題]Pauseからうまく復帰できない

回転が難しいというか、3つくらいこけてるとどこでこけてるかわからんね

  • Previewを表示するSurfaceの大きさは枠はViewのサイズ、画像の大きさはTextureView.setTransformで渡すMatrixで設定(っぽい)
    • CameraSessionから飛んでくる元画像の大きさはSurfaceTexture.setDefaultBufferSize でここでは向きの調整は入れられないっぽい
    • TextureViewの縦横比は TextureView.setAspectRatio() で設定できる?
  • LANDSCAPE用の回転 + 縮小をかけている場合、POTRAITではカメラのプレビューが表示されない
  • ImageReaderの書き出す画像の向きがLANDSCAPE時に上下正しくなる向き(PORTRAITだと表示に利用する前に回転をかける必要がある)
  • カメラのデバイスがCameraSessionで飛ばしてくる画像が、端末の向きに対してどれくらい回転しているかは CameraCharacteristics.SENSOR_ORIENTATION で取れるが、カメラによって固定っぽい
  • Activityの向いている方向は getWindowManager().getDefaultDisplay().getRotation()

気になってること

  • [新しい課題]縦向きの場合と横向きの場合でちょうどいいrotationのMatrixを見つける
  • [新しい課題]縦向きの場合と横向きの場合でそれぞれちょうどよくCameraのPreviewが表示されるようにする