2

I started a for loop in an interactive bash session. For this question we can assume the loop was something like

for i in dir/*; do
    program "$i"
done > log

The command takes a lot longer than expected and still runs. How can I see the current progress of the running for loop.

Non-Solutions:

  • Look at log.
    Doesn't work because: program is expected to run silently. You can think of program as a validator. If the loop completes without any output then I'm happy.
  • Stop the loop. Add some kind of progress indication (for instance echo "$i"). Start the loop again.
    Doesn't work because: The loop already ran for hours. I don't want to throw away all the time and energy invested in the current run. I assume everything works fine. I'm just curious and want to know the current progress.
  • Ctrl+Z then set -x; fg
    Doesn't work because: bash doesn't continue the loop when using fg. After fg only the current/next command inside the loop will run, then the loop exits. You can try it yourself using for i in {1..200}; do printf '%s ' $i; /usr/bin/sleep 0.5; done.

1 Answer 1

4

Wildcards as dir/* always expand in the same order. This feature together with the current value of $i can be used to show a progress information.

The current value of $i can be retrieved by looking at the processes running on your system. The following command prints the currently running instance of program and its arguments.
If there are multiple program processes you may want to use the --parent ... option for pgrep to only match processes started by the shell running the for loop you want to inspect.

ps -o args= -fp $(pgrep program)

Extract the value of $i manually, then get your progress using

a=(dir/*)
n=$(printf %s\\0 "${a[@]}" | grep -zFxnm1 'the exact value of $i' | cut -f1 -d:) 
echo "Progress: $n / ${#a[@]}"

This works under two assumptions

  1. The content of dir/ does not change while running the loop. If program creates, renames, or deletes files then the numbers are likely wrong.

  2. Calling program starts a new process. If program is a bash function then there won't be a sub-process for program. If the function function itself calls another program that starts a new process then you can look for that sub-program. However, if program is a bash built-in (commands that are listed by help) or a pure bash function (one that uses only bash built-ins) then you are out of luck.

If you have problem 2 or your for loop has a different structure than the one in the question (for example program < "$1" or a very long loop body with many different programs) then you might be able to get some progress information from lsof by looking at the files opened by your current shell session or its child processes.

1
  • There's also the watch utility that runs a given command repeatedly, showing the output. It might be useful here.
    – ilkkachu
    Sep 21, 2019 at 14:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .