初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(7)]
alpha2
→のサイドバーだかボードだかに、Googleドライブへのリンク作りました。ぬかみそフォントも含めてダウンロードものは置いてあります。
んで、シェルスクリプト製ファイルリネームツール、Googleドライブには、書き換えた新版を置いときます。yahooのほうは古いままです。
おもにUbuntu18.04で作りましたが、最近20.04にアップグレードして、ちょっとテストしたところでは動作してます。が、アップグレードした直後にバグを見つけたりもしました。
まぁ信頼性が無いのはどっちもどっちだったりしますが。
スクリプトは大幅に書き換えていて、高速化と整理は進めました。
でも、機能を箇条書きするのだと、あんまり変わんないんすよね。
- 設定ファイルをディレクトリ一つにまとめて、設定で簡単に移動可能にした。
- スクリプト本体と日本語コメントを、ファイルで分けた。言語ファイル的なものを作って入れ替えれば、北海道弁バージョンとかも作れなくはない。
- キーカスタマイズもファイルに分離した。テンキー有りと無しをファイルで入れ替えることができる。
同梱した説明書に、そこそこ詳しく中身について書いてます。まぁ自分用なんだけど。
使い方を理解したうえで無理なく使うようにすれば、一万ファイルくらいなら実用範囲内だと思います。
凝ったことをすると、途端に遅くなるけれど、古い版よりはまぁ、そこそこ体感速度は改善してます。
初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(6)]
#!/bin/bash # ============================================ # 配列は1つずつ読み出すべきか? # ============================================ echo '配列を3つ作る' ARR1=(`seq 0 1234567`) ARR2=(`seq 0 1234567`) ARR3=(`seq 0 1234567`) SECONDS=0 echo '1つのforループの中で、3つの配列を読み出す / 10' for N in `seq 0 123456` ; do echo ${ARR1[N]} ${ARR2[N]} ${ARR3[N]} done 1>/dev/null echo ${SECONDS} read SECONDS=0 echo '1つのループで1つの配列を読み出すx3' for N in `seq 0 1234567` ; do echo ${ARR1[N]} done 1>/dev/null for N in `seq 0 1234567` ; do echo ${ARR2[N]} done 1>/dev/null for N in `seq 0 1234567` ; do echo ${ARR3[N]} done 1>/dev/null echo ${SECONDS} read SECONDS=0 echo 'pasteとプロセス置換' for N in `seq 0 1234567` ; do echo ${ARR1[N]} done | paste -d " " - \ <( for N in `seq 0 1234567` do echo ${ARR2[N]} ; done ) \ <( for N in `seq 0 1234567` do echo ${ARR3[N]} ; done ) >/dev/null echo ${SECONDS}
配列を3つ作る 1つのforループの中で、3つの配列を読み出す / 10 328 1つのループで1つの配列を読み出すx3 42 pasteとプロセス置換 32
「配列1/配列2/配列3」って感じで、配列を区切り文字で一列にして、標準出力から出す、というのをやってたら、なんか遅くなったなと。書き換えたときに。
ループの中で複数の配列を一つずつ読み出すと、結果だけ見れば、かなり遅い。
プロセス置換すれば複数コアを使ってくれてるけど、あんまり、こういう書き方はしたくないやね。速くても。
Ubuntu18.04のbashの話です。上のは。バージョン4、何だったか。
Ubuntu20.04にアップグレードしたら、上記の問題は消えました。普通に一つのループで全部読みだすのが、いちばん早い。
なんかあったんすかね。改善とかいろいろと。
初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(4)]
無いとは断言できんのだけど
サイドバーの「ぬかみそフォント」のところに添えて、シェルスクリプトのリネームツールをzipファイルにまとめて置いてみました。昨日の夜。
で、ですね。いま試したら、Ubuntu環境のFirefoxでダウンロードすると、マルウェア扱いになるみたいです。WindowsのFirefoxだと何も言ってこないのだけど……。
いやまぁ確かに、おっかないっすけどね。野良シェルスクリプトなんて。
……っつーか、さらに今ためしたら、フォントのほうまでマルウェア扱いされたんすけど……ずいぶん長いこと置いてるんだが、今更そんなこと言われても。……あーあ、Chromeも同じこと言ってくるし。
シェルスクリプトはともかく、こっちもかい。どうにもならんなこれ。
追加の追記
drive.google.com
Googleドライブに置いてみたら、マルウェア扱いが消えた。Yahooのサービスに置いてるのと、まったく同じなんすけど。
Yahooの信用が無いってことはないし、なんだろこの状況。
できるかな。 pic.twitter.com/v3K2VnyU0Q
— Narr(公開アカウント) (@beta_reverse_2) 2020年6月27日
初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(3)]
パイプを再帰で繋いでみるテスト
#!/bin/bash : << '#----------------------------------------------------------------------------------comment' ■ ミニburdock --onceのみ パイプの再帰つなぎテスト #----------------------------------------------------------------------------------comment pipeRecursiveOnceFunc(){ # 位置パラメータが1個以上あるならば、 if test "${#}" -ge 1 ; then # 位置パラメータ${1}を変数に受け取って、ひとつshift CMD_1D="${1}" shift 1 else # 位置パラメータが無いなら、標準入力をそのまま標準出力する cat - exit 0 fi # 標準入力をreadで1行ずつ受け取る while IFS= read -r LINE ; do # 元位置パラメータを展開、evalでコマンドとみなして、 # 標準入力から来たデータをコマンドに入力、 # そのまま標準出力する eval 'echo "${LINE}" | '"${CMD_1D}" # whileループからの標準出力を、すべて、この関数自身に再帰入力 # 位置パラメータは、shiftで一個減らしたものを # ふたたび位置パラメータに全て与える done | pipeRecursiveOnceFunc "${@}" } pipeRecursiveOnceFunc "${@}"
#!/bin/bash : << '#----------------------------------------------------------------------------------comment' ■ ミニburdock --loopのみ パイプの再帰つなぎテスト #----------------------------------------------------------------------------------comment pipeRecursiveLoopFunc(){ if test "${#}" -ge 1 ; then CMD_1D="${1}" shift 1 else cat - exit 0 fi eval 'while IFS= read -r LINE ; do echo "${LINE}" done | '"${CMD_1D}"' | pipeRecursiveLoopFunc "${@}"' } pipeRecursiveLoopFunc "${@}"
簡易型の『burdock』のテストで、標準入力された文字データを、引数に与えたコマンドでevalして加工していく。
昨日の「(2)」で書いた、パイプでつないで並列処理できねーかな、の実装例として、試しにやってみた。
位置パラメータが単純にコマンドの文字列だけだってんなら、こうやって簡単に書けるんだけど。他のオプションをどうやって処理するかだとか、パイプの先にシェル変数がどこまで受け継がれてくれるか、だとか。
そのへんの判断が、シェルの仕組みに詳しくない私だと、難しくてどうとも。
並列処理になって、いるような気はするんだけど、なんかちょっと、重い気がする。並列にはなっても結局は重いんだったら、実用性は無いんだし。
確かに昨日書いたような、コマンドの文字列を作ってeval、よりは、こっちのほうがなんとなく格好いいことをしてる、ように見えるんだけどもさ。再帰って、なんかおっかない。
勉強目的なら、このやり方でやってみるのは悪くないとは思うんだけども。重くても、難しくてトラブルまみれでも、いいなら。
初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(2)]
whileとforのパイプ繋ぎ
- for.sh
#!/bin/bash stdin=`cat -` for LINE in ${stdin} ; do expr ${LINE} + 1 #echo $(( ${LINE} + 1 )) done
- while.sh
#!/bin/bash while read LINE ; do expr ${LINE} + 1 #echo $(( ${LINE} + 1 )) done
$ time seq 0 1300 | ./for.sh 1>/dev/null real 0m1.022s user 0m0.868s sys 0m0.173s $ time seq 0 1300 | ./while.sh 1>/dev/null real 0m1.028s user 0m0.828s sys 0m0.221s $ time seq 0 1300 | ./for.sh | ./for.sh | ./for.sh | ./for.sh | ./for.sh 1>/dev/null real 0m5.203s user 0m4.089s sys 0m1.240s $ time seq 0 1300 | ./while.sh | ./while.sh | ./while.sh | ./while.sh | ./while.sh 1>/dev/null real 0m2.683s user 0m3.725s sys 0m1.395s
whileだと、1行をexprで計算するごとに、1行ずつ出力している。forでは、すべての数字を計算してから、まとめて出力している。
forでは単純に足し算、以上の時間がかかるが、whileでは同時処理をしてくれる。
という、簡単なテストをしてたんだが。
「(1)」に置いてるリネームツールは、forループのほうなわけっすよ。配列に入れたファイル名と拡張子を、コマンドひとつにつき一回ずつ呼び出して、配列に入れなおしている。同時処理はしていない。
もともと、ファイル一つにつきコマンドを一回呼び出す処理、オプション「--once」で、burdockは非常に遅い。これを今のスクリプトのまま改善したいなら、たとえばbashの内部コマンドだけで処理を書いてシェル関数にするとか、いっそbusyboxにするか、とかになるんだけれど。それは限度がある。
なら、まとめてパイプで処理できないかと。「--once」の処理を何度も繰り返すときなら、効果がありそうなんだが。
こんなん考えてるから、いつまでたっても安定せん。
今回は、bashの配列を使ってみよう、evalやIFS変更はなるべく避けよう、多少の遅さよりも読みやすさ優先、で始めたから、あんまりゴチャゴチャにしたくないんすよ。パイプに繋いで複数のコマンドを並列処理させるってなら、どうやって書けばいいだろかなぁ。パイプを繋いだコマンドの文字列を作って、evalで実行するくらいしか、やり方を思いつかん。
現実の道具としては、bash専用の自分用スクリプトなら、高速化のためにevalを乱用するより、読みやすさを重視したほうがいいと思う。
bashに限定せずに他のシェルでも動くようにするのなら、多少の読みにくさ、トラブルの起きやすさは、しょうがない。しょうがないんだけど……readコマンドがbashのでしか使い物にならないから、やっぱりforにするしかない。パイプで繋いでいても、スクリプトの中身がforなら、並列動作になんないんだから、パイプにする必要もないんじゃなかろうか。
書くなら、dashとbusyboxで使えるバージョンのほうかなぁ。