10
4

More than 5 years have passed since last update.

BashScript を Elixir で書き直してみたっ(2倍速〜)

Last updated at Posted at 2018-12-15

(この記事は「fukuoka.ex Elixir/Phoenix Advent Calendar 2018」の15日目です)

昨日は@zacky1972さんの
ZEAM開発ログ2018年総集編その2: Elixir 研究構想についてふりかえる(後編)
でした!


この記事は「古い写真のアルバムをスマホで見るようにしたお話」の後日談です





IMG_8367.jpeg

Summary

Exifデーターを編集するBashScriptのコードをElixirで書いてみました
実行時間が約半分になったよ

実行時間の比較

Elixir

$ time elixir foo.exs

real    5m48.747s
user    0m2.563s
sys     0m1.753s

BashScript

$ time bash foo.bash

real    9m2.675s
user    7m19.833s
sys     1m14.571s

Motivation

「古い写真のアルバムをスマホで見るようにしたお話」で写真を整理するためにBashScriptを書いたのですが結構遅かったので、もう少し早くならないかな〜と思ったので、、、

ってElixirってなに?

なんでしょう? callmekoheiもよくはElixirを知りません(すいませんっ)

今時点callmekoheiElixirについて知っていることは

  1. スクリプト言語だよ(手軽に書けるよ)
  2. 左から右にるるるる〜という言語だよ(|>があるよ)
  3. F#という言語にすごく似てるよ

ぐらいですっ

ってなんでElixir?

しょっちゅうTwitterのタイムラインに流れてくるからです

@piacere_ex さんというかたをフォローしたら怒涛のごとくElixirの情報が流れてきました(笑)

もくもく会というのがあって先日参加させていただいたらElixirに興味が出てきました!

fukuoka ex のもくもく会はここを覗いてみてください!
---> fukuokaex もくもく会

Elixir で Exif データーを編集してみる

F#を横目にElixirで「こんにちは世界」Elixirの「こんにちは世界」ができたのでさっそく実際にコード書いてみました!

古い写真のアルバムをスマホで見るようにしたお話 05-05 BashScriptで連番振りスクリプト書いてみた で書いたBashScriptのコードをElixirで書き直してみました!(もっといい方法があるかもしれません・・・・)

defmodule Foo do

  def sortedFileNameList( fp ) do

    exifToolParam = [
      "-quiet",
      "-fast2",
      "-s3",
      "-filename",
      "-fileorder datetimeoriginal",
      "-fileorder filename"
    ]

    bashCommand = [
      "-c",
      "cd " <> fp <> " ; echo *jpg | xargs exiftool " <> Enum.join( exifToolParam , " " )
    ]

    System.cmd("bash" , bashCommand )
    |> elem(0)
    |> ( fn x -> String.split(x ,"\n") end ).()
    |> Enum.drop( -1 )

  end

  def resetAllTime( fp ) do

    exifToolParam = [
      "-quiet",
      "-fast2",
      "-s3",
      "-d '%Y:%m:%d 00:00:00'",
      "-overwrite_original_in_place",
      "\"-alldates<alldates\""
    ]

    bashCommand = [
      "-c",
      "cd " <> fp <> " ; echo *jpg | xargs exiftool " <> Enum.join( exifToolParam , " " )
    ]

    System.cmd("bash" , bashCommand )
    |> elem(0)

  end

  def addOneSecond( fp ) do

    exifToolParam = [
      "cd " <> fp <> " ; exiftool",
      "-quiet",
      "-fast2",
      "-s3",
      "-overwrite_original_in_place -alldates+=0:0:"
    ]

    exifToolParam2 =
      Foo.sortedFileNameList( fp )
      |> Enum.with_index()
      |> Enum.map( fn { filename , idx } ->
        Enum.join( exifToolParam, " ") <> Kernel.inspect(idx) <> " " <> filename
      end )

    exifToolParam2
    |> Enum.each( &( System.cmd("bash",["-c" , &1]) ))

  end

  # 確認用
  def myCheck( fp ) do

    exifToolParam = [
      "-quiet",
      "-fast2",
      "-s3",
      "-filename",
      "-datetimeoriginal",
      "-fileorder datetimeoriginal",
      "-fileorder filename"
    ]

    bashCommand = [
      "-c",
      "cd " <> fp <> " ; echo *jpg | xargs exiftool " <> Enum.join( exifToolParam , " " )
    ]

    System.cmd("bash" , bashCommand )
    |> elem(0)
    |> ( fn x -> String.split(x ,"\n") end ).()
    |> ( fn lst -> lst -- [""] end ).()

  end

  def jpgFolderList(fp) do
      Path.wildcard( Path.expand( fp ) <> "/**/*.jpg" )
      |> Enum.map( &( Path.dirname( &1 )))
      |> Enum.uniq
  end

  def mainImpl( foo ) do

    " sortedFileNameList " |> IO.puts
    Foo.sortedFileNameList(foo)

    " resetAllTime " |> IO.puts
    Foo.resetAllTime(foo)

    " addOneSecond " |> IO.puts
    Foo.addOneSecond(foo)

    " myCheck " |> IO.puts
    Foo.myCheck(foo) |> Enum.each( &( IO.puts( &1 )))

  end

  # 写真データーのExifデーターを変更できるようにするために権限を変える
  def chmod644(fld) do
    System.cmd("bash" , ["-c", "cd " <> fld <> " ;  chmod 644 *jpg"] )
  end

end

defmodule Main do

  # 写真データーがあるフォルダを指定する
  baseFolder = "/Users/callmekohei/Desktop/tmptmp"

  # すべての写真データーのpermissionを644にする
  Foo.jpgFolderList( baseFolder )
  |> Task.async_stream( &( Foo.chmod644( &1 ) ) ,[ timeout: :infinity, max_concurrency: 1000] )
  |> Enum.to_list()

  # メイン部分を実行
  Foo.jpgFolderList( baseFolder )
  |> Task.async_stream( &( Foo.mainImpl( &1 ) ) ,[ timeout: :infinity, max_concurrency: 1000] )
  |> Enum.to_list()

end

まえに書いたBashScriptより早くなってますね。パラレルが効いてるのでしょうか?

はまったところ

下記のようにTask.async_streamを2重に使うと扱うデーター数が多くなるとタイムアウトでプログラムが止まってしまいます。今回の場合フォルダ数が5個以上の時にエラーが頻発しました


  def addOneSecond( fp ) do
    exifToolParam2
    |> Task.async_stream( &( System.cmd("bash",["-c", &1]) ))
    |> Enum.to_list()
  end


  Foo.jpgFolderList( baseFolder )
  |> Task.async_stream( &( Foo.mainImpl( &1 ) ) ,[ timeout: :infinity, max_concurrency: 1000] )
  |> Enum.to_list()

こんな感じのエラーがでます

15.png

(エラー回避)
内側の部分のアシンクを外します

  def addOneSecond( fp ) do
    exifToolParam2
    |> Enum.each( &( System.cmd("bash",["-c" , &1]) ))
  end

感想

ほむほむ

Elixir

いい感じ

冒頭のイラスト

ざっきー先生(山崎 進先生 @zacky1972さん )という、ヘイスガというGPUでエリクサーの演算をするライブラリ?を開発されているすごい方です!この前もくもく会でお見かけした時はずっとチョコフレーク?をもぐもぐされてましたよ。

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4