Quantcast
Channel: fukuoka.ex Elixir/Phoenix Advent Calendarの記事 - Qiita
Viewing all articles
Browse latest Browse all 25

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

$
0
0

(この記事は「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でエリクサーの演算をするライブラリ?を開発されているすごい方です!この前もくもく会でお見かけした時はずっとチョコフレーク?をもぐもぐされてましたよ。


Viewing all articles
Browse latest Browse all 25

Trending Articles