LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   bash - difference between executing program in for loop and typing each command? (http://www.linuxquestions.org/questions/programming-9/bash-difference-between-executing-program-in-for-loop-and-typing-each-command-681561/)

jlarsen 11-06-2008 11:19 AM

bash - difference between executing program in for loop and typing each command?
 
I'm trying to use a script to call a program (which is done in C by somebody else), and have trouble when trying to use a for loop.

The program being called by the script looks for a configuration file in the directory from which it is called. The script just changes directories then it runs the same program. The syntax of the loop seems OK, but it never gets past the first call of the program.

Basic idea is:

Code:

cd /path/to/cfgdir1
/path/to/program
cd /path/to/cfgdir2
/path/to/program

It works like that, but when I put it in a for loop it never makes it to the second call of the program, like control is never returned to the script?


Code:

#!/bin/bash
#
progdir=( ex1 ex2 ex3 ex4 ex5 )

for (( i = 0 ; i < ${#progdir[@]} ; i++ )); do
  cd /path/to/cfgdir/${progdir[$i]}
  /path/to/program
  echo "finished with ${progdir[$i]}"
  sleep 2
done
exit

I get the last output from the program being called, but I do not ever get output from echo.

Any ideas?
Thanks in advance.

jailbait 11-06-2008 12:23 PM

I gave an incorrect answer.

-----------------
Steve Stites

jlarsen 11-06-2008 12:32 PM

I have tried again with a simplified version of the script to make troubleshooting a little easier, but still the same result. Here is the new version:

Code:

#!/bin/bash
#
for progdir in ex1 ex2 ex3 ex4 ex5; do
  cd /path/to/cfgdir/$progdir
  /path/to/program
  echo "finished with $progdir"
  sleep 1
done

exit

I'm not positive if it is the script or that the C program being called needs to exit properly to give control back to the script. Still puzzled why it works when typing the instructions line by line in a script though......maybe the for loop forks the process and it never comes back? Dont' know, I have limited understanding of how all that works.

jan61 11-06-2008 01:16 PM

Moin,

first you should rename your loop counter. test is a shell builtin, you get into trouble using it as a variable name.

Jan

jlarsen 11-06-2008 01:22 PM

Thanks for the reply, but I did not use test in the actual script. I was just trying to make it easier to read. I will edit original post to reflect this.

jlarsen 11-06-2008 01:29 PM

Quote:

Originally Posted by jlarsen (Post 3333670)
maybe the for loop forks the process and it never comes back?

So much for that theory, I've been reading the Bash Guide at gnu.org and found this:

Quote:

Since Bash is a completely new implementation, it does not suffer from many of the limitations of the SVR4.2 shell. For instance:

* Bash does not fork a subshell when redirecting into or out of a shell control structure such as an if or while statement.


jan61 11-06-2008 01:29 PM

Moin,

Quote:

Originally Posted by jlarsen (Post 3333715)
Thanks for the reply, but I did not use test in the actual script. I was just trying to make it easier to read. I will edit original post to reflect this.

place a "set -x" line before the loop, this tells the bash to write every command, like it's invoked, on stderr. Watch the output and post it here if you don't find the reason for the "unexpected behaviour".

Jan

jlarsen 11-06-2008 05:17 PM

While looking at the output generated by running the script with "set -x", I found some other output I missed earlier from the program that is called by the script.

Code:

Trying to kill an old running myprogram process ID='29243'
Terminated
root@bkloaner:/path/to/myprogram#
 Killed old 'myprogram' process ID='29243'

Turns out the program being called checks to see if there another instance of itself before starting, and then kills it. I don't have any control over the way that program works, so now my question is:

How do I make sure that the first instance of the program myprogram is allowed to finish running before the next is started?

chrism01 11-06-2008 05:35 PM

You could try
Code:

ps -ef|grep /path/name_of_program|grep -v grep
if [[ $? -eq 0 ]]
then
    echo "prog already running"
else
    run_it
fi

You can expand that using awk to get the pid if you want to kill it, if its safe to do so.

jan61 11-07-2008 12:45 PM

Moin,

Quote:

Originally Posted by chrism01 (Post 3333924)
...
Code:

ps -ef|grep /path/name_of_program|grep -v grep
...


A more comfortable way is pidof. And with this little extension you can forget the grep -v grep:
Code:

ps -ef|grep [/]path/name_of_program
Jan

jlarsen 11-07-2008 03:06 PM

Thanks for the tips!

I could possibly put the code given in a while loop and just keep checking until it is OK to run myprogram.

Part of the problem is that I don't understand when a child process is made, which seems to be the problem in this situation. From what I can tell


Code:

cd /path/to/cfgdir1
/path/to/myprogram
cd /path/to/cfgdir2
/path/to/myprogram

will change directories, execute /path/to/myprogram, when myprogram finishes, it will change directories again, then execute /path/to/myprogram again, etc.

The for loop, however, seems to create multiple child processes, and since /path/to/myprogram will kill any already existing instances of itself, I run into problems. Am I understanding this correctly?

If so, what I would like is a way to tell bash not to create child processes, or to wait until the first one completes before starting the second one.

I have tried using "wait" both before or after calling myprogram, but either I am using it incorrectly or this is not what I need.

The_Kernel 11-08-2008 12:19 AM

To use "wait" you need to start the preceding command in the background by adding an '&' to the end of the command. Like this:

Code:

cd /path/to/cfgdir1
/path/to/myprogram &
MY_PID="$!"
wait $MY_PID


jlarsen 11-10-2008 11:09 AM

Well, I finally figured out why the script with each line typed out (instead of a for loop) was working and it had nothing to do with the loop. The first script did not have the #!/bin/bash at the top. I removed this from the script with the for loop and now it executes each command in order, instead of making a bunch of child processes and running them at the same time.

I understand using #!/bin/bash specifies the program that interprets the commands in the script, but what is used when nothing is specified?

chrism01 11-10-2008 05:19 PM

It uses the default shell ie whichever one you are currently logged in with. You can use
echo $SHELL
or
grep yourusername /etc/passwd
to check which one.

jlarsen 11-10-2008 05:38 PM

That is strange, both of those show that I am using /bin/bash

However, once I remove the #!/bin/bash (without editing anything else) the script works. I also noticed the original script that just writes out command without the for loop does not work if I add #!/bin/bash. This may end up going unsolved, but here is the script I am using now just in case it matters to anyone:

Code:

# runs myprog for each site
#
sites=( ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex7 ex8 ex9 )

#set -x
for (( i = 0 ; i < ${#sites[@]} ; i++ )); do
  cd /path/to/different/cfg/dirs/${sites[$i]}
  /path/to/myprogram &
  MY_PID="$!"
  wait $MY_PID
done
exit



All times are GMT -5. The time now is 12:45 AM.