Search code examples
bashshellubuntuunixwindows-subsystem-for-linux

Why does the for loop in this shell script perform the first iteration twice before doing the second one?


I have a bash script:

#! usr/bin/env bash
# test bash script to call python scripts

cd ~/python
for ((i = 1; i <= 2; i++)); do
 python3 test_0$i.py; done
cd ~/bash

that pulls test_0x.py files from the python directory and runs them in order. test_01.py is a single print statement, whereas test_02 is a for loop.

I was expecting it to:

  1. navigate to ~/python
  2. run test_01.py
  3. run test_02.py
  4. navigate back to ~/bash

the bash script runs test_01.py, then test_01.py again, and only then test_02.py. is this some quirk of shell-scripts i am simply too green to anticipate, or is there something wrong with my — albeit very basic — code?

I am using an ubuntu terminal through windows subsystem for linux, if that affects things.


Solution

  • Running through WSL should have no impact in this case.

    There are a few problems with your script, but the ones that are visible, at least, wouldn't cause an additional loop.

    To debug the loop part of the problem, I'd start by:

    1. From the command-line, run the same loop in the same directory, but with an echo in place of the Python invocation itself:

      cd ~/python
      for ((i = 1; i <= 2; i++)); do
        echo test_0$i.py; done
      
    2. If that works, make the same adjustment to the script and test it.

    3. If that works, as mentioned in the comments, check test_01.py to see if it is calling test_02.py. Try running it manually at the command-line and/or visually inspect it.

    As @user1934428 mentioned in the comments, running with set -x may also yield useful information.


    Other issues

    • There is a typo in your shebang line (at least as you entered it here in your question) - #! usr/bin/env bash. The purpose of a shebang line is to tell system what program should execute your script (e.g., when called as ./script.sh). This is simply not going to work.

      The proper shebang line would be:

      #!/usr/bin/env bash
      
    • However, if you really are expecting it to run as a script, then we come to the second issue. You mention that step 4 should be to "navigate back to ~/bash".

      This will not have the intended effect when executing a script via the shebang line. It may appear to work, but what really happens is that the shell will still be in whatever directory it started in. This may be the ~/bash directory, but it may not be. For example:

      cd ~
      ~/bash/script.sh
      

      This would leave you back in the ~ directory after the script completes.

      Why? When using a shebang line, the script is executed by a separate shell invocation. It's inside this shell (running as a subprocess) that the cd takes place. As a cd is an environment change (the PWD environment variable), it can't be propagated back to the calling process.

      If you really need the script to be able to change the directory for the calling shell (and you probably don't), then you would need to source the script instead of executing it. source script.sh will process the script inside the currently running Bash shell, allowing it to change the current directory of the current shell.