初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(8)]

alpha3

→のGoogleドライブに、アルファ版3を置きました。2とあんまり変わってません。高速化目的で配列をややっこしい書き方してたところを、普通の書き方に戻してます。bash5なら、こっちのほうが早い。
気力が失せつつあるんで、たぶんこれでしばらくは更新しません。

下に、説明書の一枚をコピーして置いときます。画像ファイルは抜けてます。はてなフォトがwebpに対応してないから、面倒なもんで。

続きを読む

初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(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にアップグレードしたら、上記の問題は消えました。普通に一つのループで全部読みだすのが、いちばん早い。
なんかあったんすかね。改善とかいろいろと。

初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(5)]

そのまんまで済まんっすが。仕様のメモのHTMLです。
大部分は、スクリプトを見ないと理解できないけど。

joinコマンドでリネームの重複を検査する、のが工夫、かな?
シェルスクリプトでファイルのリネームツールを作るとき、リネームの途中でファイル名が同じものが出来てしまうと困るわけっす。それをリネーム実行前に検査するのを、『burdockUI』ではどうやってるか、のメモが書いてある。
ちゃんと動くのか、いちばん大事なそこが曖昧だけども。
でも、動作速度については、かなり早いっす。10000ファイルのリネームを検査するくらいは、余裕でいけます。

続きを読む

初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(4)]

無いとは断言できんのだけど

サイドバーの「ぬかみそフォント」のところに添えて、シェルスクリプトのリネームツールをzipファイルにまとめて置いてみました。昨日の夜。
で、ですね。いま試したら、Ubuntu環境のFirefoxでダウンロードすると、マルウェア扱いになるみたいです。WindowsFirefoxだと何も言ってこないのだけど……。
いやまぁ確かに、おっかないっすけどね。野良シェルスクリプトなんて。

……っつーか、さらに今ためしたら、フォントのほうまでマルウェア扱いされたんすけど……ずいぶん長いこと置いてるんだが、今更そんなこと言われても。……あーあ、Chromeも同じこと言ってくるし。
シェルスクリプトはともかく、こっちもかい。どうにもならんなこれ。

追加の追記

drive.google.com
Googleドライブに置いてみたら、マルウェア扱いが消えた。Yahooのサービスに置いてるのと、まったく同じなんすけど。
Yahooの信用が無いってことはないし、なんだろこの状況。


初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(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で使えるバージョンのほうかなぁ。