内容
- shellでのバックグラウンド処理
- ジョブ制御フラグ
- メタ文字による標準出力の管理
Android - GradleをZshで補完する - Qiitaという記事を書いた時に
なお、初回のみタスクの読み込みに時間がかかるが、2回目以降はファイルキャッシュを読み込むため比較的速くなる
と書いたが、初回の読み込み時の長さが気になるので読み込み中であるindicatorを表示することにした
参考
Pythonでローディングのぐるぐるを表示する - CAMPHOR- Tech Blog
完成品
./gradlew
のあとでTAB
を押すとtasks
を読み込む
読んでる間の時間が長いので、indicatorを出して読み込み中であることを示している
コード
gistも更新した
#!/usr/bin/env zsh
# refs: https://gist.github.com/nolanlawson/8694399 : bash-version
loading() {
local count=30
if [ $# -eq 1 ]; then
count=$1
fi
# KILL -INT をtrapして最後の出力を消す
trap "echo -en '\r '; exit 0" INT
# 改行
echo
for i in `seq 1 1 $count`
do
echo -en '|\r' ; sleep 0.05;
echo -en '/\r' ; sleep 0.05;
echo -en '-\r' ; sleep 0.05;
echo -en '\\\r'; sleep 0.05;
done
exit 0
}
_gradle() {
local cur="$1"
local gradle_cmd='gradle'
if [[ -x ./gradlew ]]; then
gradle_cmd='./gradlew'
fi
if [[ -x ../gradlew ]]; then
gradle_cmd='../gradlew'
fi
local completions=''
local cache_dir="$HOME/.gradle_tabcompletion"
mkdir -p $cache_dir
local gradle_files_checksum='hoge';
if [[ -f build.gradle ]]; then # top-level gradle file
if [[ -x `which md5 2> /dev/null` ]]; then # mac
local all_gradle_files="$(find . -name build.gradle 2> /dev/null)"
gradle_files_checksum="$(md5 -q -s "${all_gradle_files}")"
else # linux
gradle_files_checksum="$(find . -name build.gradle | xargs md5sum | md5sum)"
fi
else
gradle_files_checksum='no_gradle_files'
fi
if [[ -f $cache_dir/$gradle_files_checksum ]]; then # cached! yay!
completions=$(\cat $cache_dir/$gradle_files_checksum)
else
################################
# ジョブ制御を無効化
set +m
# バックグラウンドでindicatorを回す
loading 1000 & >/dev/null 2>&1
set -m
# indicatorのprocess id
local LOADING_PID=$!
completions=$($gradle_cmd --no-color --quiet tasks | grep ' - ' | awk '{print $1}' | tr '\n' ' ')
# indicatorを殺す
kill -INT $LOADING_PID
################################
if [[ ! -z $completions ]]; then
echo $completions > $cache_dir/$gradle_files_checksum
fi
fi
local -a commands
commands=( "${(z)completions}" )
compadd $commands
return 0;
}
compdef _gradle gradle
compdef _gradle gradlew
compdef _gradle ./gradlew
読み込み中の表示
loading
関数は引数を1つとってその引数回数だけindicatorを回転させるものとなっている
while
を使って無限ループにしても良いと思う
\r
は復帰文字と呼ばれるもので、標準出力で今いる行の先頭にカーソルを動かすイメージのもの
\b
はバックスペース
参考:シェル・スクリプト・リファレンス - 【 メタ文字の取り扱い 】:ITpro
くるくる回る棒を2>&1
して標準エラー出力にリダイレクトしても良いが、どうせ後で消すのでここではしなかった
この読み込み中のものだけ切りだすとこんな感じ
#!/usr/bin/env zsh
loading() {
local count=30
if [ $# -eq 1 ]; then
count=$1
fi
for i in `seq 1 1 $count`
do
echo -en '|\b' 1>&2; sleep 0.05;
echo -en '/\b' 1>&2; sleep 0.05;
echo -en '-\b' 1>&2; sleep 0.05;
echo -en '\\\b' 1>&2; sleep 0.05;
done
# 最後の出力を消す
echo -en ' \b' 1>&2;
exit 0
}
loading $@
新しいバージョンだとBash
でも動きます
読み込み中を隠す
普通に&
をつけてバックグラウンド処理をするとバックグラウンドに移った処理のプロセスIDが表示されてしまいます
鬱陶しいのでset +m
としてジョブ制御をしないフラグを立てることでこれを表示しないように出来る
参考: Linuxコマンド集 - 【 set 】 シェルのオプションを設定する:ITpro
直前に実行したプロセスのIDは$!
で取得出来るので、
これに対して読み込み中の表示を消したいタイミングでkill
を送ってプロセスを殺す
kill
で送られてきたシグナルを
trap "echo -en '\r '; exit 0" INT
のようにtrap
でINT
シグナルを受け取って標準出力を掃除してからexit
するようにしている
心残り
どうしても
[8] + done loading 1000
といったkill
時に表示されるメッセージを消せなかった
これはkill
した側のshellで表示されるらしく、kill
する部分をサブプロセスにするなど、手を尽くしたがだめだった