0

Say I have this basic bash script for installing Vim plugins:

#!/usr/bin/env bash

plugins=(
tpope/vim-endwise
tpope/vim-fugitive
tpope/vim-surround
tpope/vim-unimpaired
)

rm -rf $HOME/.vim/pack/bundle/*
mkdir $HOME/.vim/pack/bundle/start

installplugin() {
  plugin=”$(echo “$1" | sed -e ‘s/.*[\/]//’)”
  git clone –depth=1 -q https://github.com/$1.git \
    $HOME/.vim/pack/bundle/start/$plugin
  rm -rf $HOME/.vim/pack/bundle/start/$plugin/.git*
  echo $plugin installed!
}

for repo in ${plugins[@]}; do
  installplugin “$repo” &
done

wait

It clones each repository in the array plugins to ~/.vim/pack/bundle/start, and does so asynchronously.

Ignoring the async element of the script (the & at the end of installplugin “$repo” &) for the moment, how would I add a . to the end of a line such as Installing $plugin (which would show at the start of the installation process of a given plugin) every second or so, as a form of progress bar, perhaps outputting Done. on the same line after completion of that plugin, then moving on to the next plugin?

Here is my stab at the issue:

#!/usr/bin/env bash

plugins=(
tpope/vim-endwise
tpope/vim-fugitive
tpope/vim-surround
tpope/vim-unimpaired
)

rm -rf $HOME/.vim/pack/bundle/*
mkdir $HOME/.vim/pack/bundle/start

progressbar() {
  until [ $installed -eq 1 ]; do
    sleep 0.1
    echo -n '.'
  done
}

installplugin() {
  installed=0
  echo -n "Installing $plugin"
  progressbar &
  plugin=”$(echo “$1" | sed -e ‘s/.*[\/]//’)”
  git clone –depth=1 -q https://github.com/$1.git \
    $HOME/.vim/pack/bundle/start/$plugin
  rm -rf $HOME/.vim/pack/bundle/start/$plugin/.git*
  installed=1
  echo ' Done.'
}

for repo in ${plugins[@]}; do
  installplugin “$repo”
done

wait

It does not work, but I do not understand why.

What I assume is a simple thing to solve becomes much more complicated if you remember that the original script was asynchronous, as you would have to remember how many lines up the output of each Installing message is from the bottom, and then update the lines with dots until their respective plugin has been installed. I need to modify the original script to show the basic form of progress bar I described earlier?

1
0

It does not work since the shell variable installed is two different variables in the progressbar function (in its subshell environment) and in the installplugin function. The progressbar function is executed in a subshell since you start it as a background task. It will inherit the value from its parent environment when the function is started, but the parent will not be able to set a new value in the child environment (the progressbar subshell).

This still uses progressbar as a background task, but lets it set up a trap to exit the infinite loop. The main function, foo, triggers the trap by sending it the appropriate signal when it's done.

progressbar () {
    trap 'break' USR1

    while printf '.' >&2; do
        sleep 0.25
    done
}

foo () {
    progressbar & pid="$!"

    echo 'working...'
    sleep 5
    echo 'done.'

    kill -s USR1 "$pid"
}

foo
5
  • Wow, thank you! This works perfectly ignoring the async part of the script. How would I adapt it so it works when running foo & multiple times? If it helps, it is known how many times foo will be run. Dec 14 '18 at 21:50
  • @AramisRazzaghipour How would you envisage multiple progress bars being displayed?
    – Kusalananda
    Dec 14 '18 at 21:54
  • Basically working... is echoed multiple times, each on its own line, and then the dots from progressbar appear on each line independently. I’m guessing that the \e[xA escape sequence will be used to overwrite specific lines? Dec 14 '18 at 21:57
  • @AramisRazzaghipour You would probably need some sort of curses library to do that. Any implementation that just prints dots in a background subshell can not be aware of how many other progress bars are currently running concurrently. I might get back to this late though.
    – Kusalananda
    Dec 14 '18 at 22:00
  • Hmm, it seems the recommended way to do something like this involves temporary files … Dec 14 '18 at 22:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.