Thread: exec a bash script

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Registered User
    Join Date
    May 2011
    Posts
    116

    exec a bash script

    hello!

    I'm writing a C++ program and I use fork() and execl to call a bash script.My .cpp communicates with the script with a pipe in which the script writes data and then the .cpp reads them.I don't think my script is ever executed the way I'm doing it...

    Code:
            int pid;
    	int res = mkfifo (pipeName, 0666); 
    	if (res < 0) {
    		perror("Error creating the named pipe");
    		exit (2);
    	}
    	
    	pid=fork();
    	if(pid<0)
    	{
    		cerr << "Failed to fork" << endl;
    		exit(1);
    	}
    
    	if(pid==0){
    		
    		execl("./bbb","bbb",argv[1],pipeName,NULL);
    		exit(0);
    	}
    where pipeName is the name of the pipe .cpp and bash script communicate with,argv[1] is just a string,bbb is the name of the script

    In my script when I want to write something to the pipe I do

    Code:
    echo $var2 >> $2
    where $2 is the name of the pipe the script got when called.

    When I run it it seems to meet a deadlock so I figured maybe the script is never called...
    Any ideas?

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    A bash script should not be executed directly with execl. It is a file that is interpreted by the bash shell. So you need to execute the bash shell, with the name of a script as first argument (and subsequent arguments after that).

    If the bash shell is not installed on your system, all bets are off.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  3. #3
    Registered User
    Join Date
    May 2011
    Posts
    116
    Quote Originally Posted by grumpy View Post
    A bash script should not be executed directly with execl. It is a file that is interpreted by the bash shell. So you need to execute the bash shell, with the name of a script as first argument (and subsequent arguments after that).

    If the bash shell is not installed on your system, all bets are off.
    Could you please give me an example of what you mean exactly?
    Actually now my execl works fine and the script does what it has to do (write to files)
    But when I change the output the script writes to and instead of a file I choose the pipe that .cpp and script use to communicate (the name of the pipe is passed to script as an argument of execl) the script isn't working..

    I do exactly the same I did in order to write to file:

    Code:
    echo $line >> tempfile
    --tempfile is a simple file my script writes to---

    Code:
    echo $line >> $2
    --$2 is the name of the pipe the script takes as an argument--

  4. #4
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by quo View Post
    Could you please give me an example of what you mean exactly?
    His point, (or what I understood ) was to do something like this :
    Code:
    execl("/bin/bash","bash","/home/manasij7479/init.sh",nullptr);
    (Your other args go between the script's name and the null pointer)
    Last edited by manasij7479; 06-01-2012 at 05:47 AM.

  5. #5
    Registered User
    Join Date
    May 2011
    Posts
    116
    Quote Originally Posted by manasij7479 View Post
    His point, (or what I understood ) was to do something like this :
    Code:
    execl("/bin/bash","bash","/home/manasij7479/init.sh",nullptr);
    (Your other args go between the script's name and the null pointer)
    ok thank you
    but since it works now do I have to change it?
    Also,how can a script write to a pipe that was created in the .cpp and sent to the script through exec?

  6. #6
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by quo View Post
    ok thank you
    but since it works now do I have to change it?
    I don't know if the 'working' is standard or accidental.
    Also,how can a script write to a pipe that was created in the .cpp and sent to the script through exec?
    Your way of creating a pipe specifies a filename (and its full path). Just use normal File IO from the script.
    The 'file' is created as some sort of special object for the communication.
    Read "man 3 mkfifo" if you're confused.

  7. #7
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Hey gang:

    Code:
    #!/bin/bash
    Shebang. Presuming the script starts with that (they usually do), you do not need to invoke "bash script.sh"; the shebang is not interpreted by the shell, it's interpreted by the "loader", which according to this is the (kernel) handler for the execve() system call:

    Loader (computing) - Wikipedia, the free encyclopedia

    See also:

    Shebang (Unix) - Wikipedia, the free encyclopedia

    Y'all should really test your premises before you give advice .

    Quote Originally Posted by quo View Post
    since it works now do I have to change it?
    If your script starts with a shebang, you don't have to change it; grumpy was jumping to an erroneous conclusion. Using the version explicitly invoking bash will work, but it amounts to exactly the same thing and is not at all necessary. No one writes shell scripts without a shebang at the beginning.

    Also,how can a script write to a pipe that was created in the .cpp and sent to the script through exec?
    It's a named fifo, right? Open it and write to it like a file.
    Last edited by MK27; 06-01-2012 at 07:58 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  8. #8
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by MK27 View Post
    Shebang. Presuming the script starts with that (they usually do), you do not need to invoke "bash script.sh"; the shebang is not interpreted by the shell, it's interpreted by the "loader", which according to this is the (kernel) handler for the execve() system call:

    Y'all should really test your premises before you give advice .
    Doesn't that only apply for those exec functions taking a file as the argument ? .and not a path for the binary ?
    Quote Originally Posted by man 3 exec
    int execl(const char *path, const char *arg, ...);
    int execlp(const char *file, const char *arg, ...);
    int execle(const char *path, const char *arg,
    ..., char * const envp[]);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execvpe(const char *file, char *const argv[],
    char *const envp[]);
    Last edited by manasij7479; 06-01-2012 at 07:58 AM.

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by manasij7479 View Post
    Doesn't that only apply for those exec functions taking a file as the argument ? .and not a path for the binary ?
    A path to a binary is a file path; a binary is a file. These are the same thing (note, the script must have a shebang indicating the interpreter and be marked as an executable, which shell scripts usually also are). There is not one loader* for execve and then some other loader for everything else, it is just that the "e" functions include an additional parameter (environment variables, which are also not just part of the shell, altho it is common to think of them in relation to shell access via globals).

    Certainly this works fine:

    Code:
    #include <stdio.h>
    #include <unistd.h>
    
    int main(void) {
    	char *args[] = { "test.sh", NULL };
    	execv("/home/scripts/test.sh", args);
    	
    	return 0;
    }
    You can try it with all the other "exec" functions too if you want...

    * note this is not the same thing as the dynamic (library) loader potentially invoked for binaries after the kernel handler/loader has loaded the binary into memory and began its work.
    Last edited by MK27; 06-01-2012 at 08:20 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  10. #10
    Registered User
    Join Date
    May 2011
    Posts
    116
    Quote Originally Posted by MK27 View Post
    It's a named fifo, right? Open it and write to it like a file.
    Thanks for your help!Since it's the first time I handle a pipe through a script I didn't open it I just wrote to it.
    How do we open a pipe in a bash script?
    thank you

  11. #11
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by quo View Post
    Thanks for your help!Since it's the first time I handle a pipe through a script I didn't open it I just wrote to it.
    How do we open a pipe in a bash script?
    thank you
    Code:
    mkfifo myfifo.fifo # you don't have to do that if it exists already
    echo "hello world" > myfifo.fifo
    The last line blocks, however; it won't return until another process reads from the pipe. But if you don't want to wait or need to synchronize, you can put & at the end to fork it into the background.
    Last edited by MK27; 06-01-2012 at 08:22 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  12. #12
    Registered User
    Join Date
    May 2011
    Posts
    116
    Quote Originally Posted by MK27 View Post
    The last line blocks, however; it won't return until another process reads from the pipe. But if you don't want to wait or need to synchronize, you can put & at the end to fork it into the background.

    yes it works now.I noticed that it blocks and it's a bit strange because I have a while loop in the script which does many echos in the pipe.On the other hand I have the following while loop in the .cpp
    which reads the lines of the pipe

    Code:
    while(child.getline(line,LINESIZE)){
    		cout<<line<<endl;		
    	}
    child.close();
    but it only prints 2lines(although in the script are many echo commands) and then blocks

  13. #13
    Registered User
    Join Date
    May 2011
    Posts
    116
    Quote Originally Posted by MK27 View Post
    Code:
    mkfifo myfifo.fifo # you don't have to do that if it exists already
    echo "hello world" > myfifo.fifo
    The last line blocks, however; it won't return until another process reads from the pipe. But if you don't want to wait or need to synchronize, you can put & at the end to fork it into the background.
    I tried to solve the deadlock so I added a sleep(1) in the while loop of getline in my.cpp and it runs ok.But the thing is that sometimes my script has many echo commands maybe 500 or more and
    if for each echo the getline sleeps for 1 then it's very slow..
    Any other suggestions?

  14. #14
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by quo View Post
    Any other suggestions?
    Is it necessary to put the echo in the while loop or could you concatenate all the data there instead then and echo it all at once afterward?
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  15. #15
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    The problem is that in the reader, you are calling getline(), which clears the pipe that echo was waiting on, and then there is a basic "race condition" between the proceses: if the bash script manages to add another line in its loop before the reader loop calls getline() again, then you are in luck. If not, there is no line to get and so getline() fails and the while loop exits.

    You could solve this problem a few different ways. You could try using & with echo in the bash loop, but that is still not a guarantee it will keep up, and if it does, it may create an undesirable number of forks.

    The simplest would be to just feed the whole file at once into the pipe:

    Code:
    cat 'aaa' > $2
    If there is a reason you need to loop through the file one line at a time, this is what I meant by concatenate the data first then send it:

    Code:
    data=""
    while read line
    do
        data=$data$line"\n"
    done < 'aaa'
    echo -e $data > $2
    The "\n" is because read chomps the newline. The -e is important otherwise echo will output a literal slash-small-n and not a newline.

    Finally, if you absolutely have to feed a line at a time into the pipe, then you can do something like this:

    Code:
    while read line
    do
        echo $line >> $2
    done < 'aaa'
    echo "***END***" >> $2
    And the reader:

    Code:
    #include <cstring>;
    
    char line[LINESIZE];  // I'm assuming something like this
    while (1) {
        child.getline(line,LINESIZE));
        if (!strcmp(line, "***END***")) break;
        cout << line << endl;
    }
    The only pitfall with this is if the bash script exits prematurely and never sends ***END***, because there may be no percievable difference in terms of error-checking in the reader (via .good(), .eof(), .fail(), .bad()) between that and just having to wait for input from the pipe.

    It also may not work, if the 'child' fstream closes or has the failbit or eofbit set when readline returns from an empty pipe. In that case you could try checking or reseting those. It may end up that you have to re-open the stream each time.

    Finally, it's also a busy loop, but probably not too bad a one.

    WRT to adding small delays, don't do it as the sole means of synchronization, but it is fine to combine it with one of the more reliable techniques above if you think it will smooth things out or to take the potential teeth out of a busy loop. You can get smaller delays than one second on linux using nanosleep():

    SourceForge.net: POSIX timers - cpwiki

    Beware the caveat about "granularity" there; don't bother with gaps less than 10ms (meaning, your 500 line file will take at least 5 seconds).
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Awk and sed bash script help
    By Annonymous in forum Linux Programming
    Replies: 19
    Last Post: 05-10-2012, 12:40 AM
  2. ssh/bash script question
    By Overworked_PhD in forum Tech Board
    Replies: 2
    Last Post: 03-30-2009, 07:48 PM
  3. Bash Script Q
    By QuestionC in forum Tech Board
    Replies: 1
    Last Post: 04-19-2007, 10:16 AM
  4. Linux: Use C to call a bash script
    By harada in forum Linux Programming
    Replies: 9
    Last Post: 10-27-2006, 01:59 PM
  5. Running 'exec' twice in one UNIX shell script
    By Zughiaq in forum Tech Board
    Replies: 2
    Last Post: 05-03-2003, 12:04 AM