LoginSignup
20
18

More than 5 years have passed since last update.

Zshで長い処理をしている間に読込中を表示する

Last updated at Posted at 2014-12-21

内容

  • shellでのバックグラウンド処理
    • ジョブ制御フラグ
  • メタ文字による標準出力の管理

Android - GradleをZshで補完する - Qiitaという記事を書いた時に

なお、初回のみタスクの読み込みに時間がかかるが、2回目以降はファイルキャッシュを読み込むため比較的速くなる

と書いたが、初回の読み込み時の長さが気になるので読み込み中であるindicatorを表示することにした

参考

Pythonでローディングのぐるぐるを表示する - CAMPHOR- Tech Blog

完成品

loading_tasks.gif

./gradlewのあとでTABを押すとtasksを読み込む
読んでる間の時間が長いので、indicatorを出して読み込み中であることを示している

コード

gistも更新した

_gradle
#!/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して標準エラー出力にリダイレクトしても良いが、どうせ後で消すのでここではしなかった

この読み込み中のものだけ切りだすとこんな感じ

loading.sh
#!/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 $@

loading.gif

新しいバージョンだとBashでも動きます

読み込み中を隠す

普通に&をつけてバックグラウンド処理をするとバックグラウンドに移った処理のプロセスIDが表示されてしまいます

loading_background.gif

鬱陶しいのでset +mとしてジョブ制御をしないフラグを立てることでこれを表示しないように出来る
参考: Linuxコマンド集 - 【 set 】 シェルのオプションを設定する:ITpro

loading_no_job.gif

直前に実行したプロセスのIDは$!で取得出来るので、
これに対して読み込み中の表示を消したいタイミングでkillを送ってプロセスを殺す
killで送られてきたシグナルを

trap "echo -en '\r                      '; exit 0" INT

のようにtrapINTシグナルを受け取って標準出力を掃除してからexitするようにしている

心残り

どうしても

[8] + done loading 1000

といったkill時に表示されるメッセージを消せなかった
これはkillした側のshellで表示されるらしく、killする部分をサブプロセスにするなど、手を尽くしたがだめだった

20
18
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
20
18