calling "system" command from C

Discussion in 'Mac Programming' started by kamthan, Jun 13, 2006.

  1. macrumors newbie

    Joined:
    Jun 13, 2006
    #1
    hi all,

    i'm trying to call the system command from a C program and trying to pass some parameters as follows:

    Code:
    #include<stdio.h>
    #include<stdlib.h>
    
    main()
    {
    
    char s[100];
    int a = 10;
    sprintf(s, "history | tail -%d", a);
    system(s);
    }
    however, this program doesnt run i.e, does not run the history command or give any errrors on compilation or execution.

    could someone please help.

    thanks,
    ankur
     
  2. Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    It something to do with the command (my guess is the pipe).

    If you simply compile the code with a single line in main:

    Code:
    system("ls -l");
    they you get the expected result.

    OK, removing the pipe and just sprinting history does not work either.

    If I replace
    Code:
    ls -l
    in the above with
    Code:
    history
    it does not work. Is history an actual command or a shell built in? Perhaps you can't call shell built-ins?

    Code:
    which history
    cannot find it so it's probably a shell built-in.
     
  3. macrumors 6502a

    Joined:
    Jun 17, 2003
    #3
    history is a built-in in bash and so cannot be treated as a command in the way the original poster intends.

    The system() exec's a command directly without using a shell at all. It is probably possible to use system() to exec bash and then use fprintf to issue the "history" instruction to bash. But this is not advisable since there are numerous security holes that exec'ing a shell opens up.

    Is there some reason why the original poster cannot just directly access the file that bash uses to store the history? ~/.bash_history

    The following code will achieve what the OP intended:
    Code:
    sprintf(s, "tail -%d ~/.bash_history", a);
    system(s);
    
     
  4. macrumors 68020

    pilotError

    Joined:
    Apr 12, 2006
    Location:
    Long Island
    #4
    Shell Commands

    Try using "bash history" also

    You need to exec the shell in order to run shell commands.
     
  5. macrumors 6502a

    Joined:
    Jun 17, 2003
    #5
    "bash history" does not work since "history" is not a shell script and that form of invoking bash implies that "history" is a script.

    The way to make that work through is to use the following:

    Code:
    sprintf(s, "echo \"history\" | bash -i | tail -%d", a);
    system(s)
    
    That is, echo the string "history" and pipe the result into the bash. Bash is invoked as an interactive shell (the -i option) and the output from bash is piped through the original tail command. An alternative that makes things a little clearer is:

    Code:
    "echo \"history | tail -%d\" | bash -i"
    
    The difference between the first string and the second is that in the second, tail is invoked by bash to operate on the output of "history" whereas in the first tail is invoked by system to operate on the output of "bash".
     
  6. macrumors 65816

    MarkCollette

    Joined:
    Mar 6, 2003
    Location:
    Calgary, Canada
    #6
    Almost all of what you're trying to do, is access shell functionality. So the real questions are: what are you actually trying to accomplish, and why are you trying to do it in C?
     
  7. macrumors 68020

    Gelfin

    Joined:
    Sep 18, 2001
    Location:
    Denver, CO
    #7
    Okay, little bit of misinformation and guessing in the thread. What you're doing won't work, but not for the reasons people seem to think.

    First, the documentation for system() will quickly clear up the notion that calling system() doesn't invoke the shell. In fact on a unixlike system that's exactly what it does. "The system() function hands the argument string to the command interpreter sh(1)."

    sh on most modern systems, including Darwin, is just a link to or copy of bash. In Darwin /bin/sh is a copy of /bin/bash. You can tell because they have different permissions (hardlinks share permissions). The two files are otherwise identical.

    The ordinary way to invoke the shell directly with a string of commands (from a shell prompt) would be to run, for instance, "bash -c history". The -c switch tells bash to accept its commands from the command line as a non-interactive shell. This is pretty much exactly what your call to system() is doing.

    If you try running "bash -c history" at the command prompt right now, you'll see why you didn't get any results. "history" does nothing in the non-interactive context. This is also why mrichmon's code sample will, in fact, work. By forcing an interactive shell with the -i switch, the history will be available.

    Depending on a number of factors you may still not be out of the woods. You may find your default shell is tcsh instead of bash. system() will still invoke bash (do NOT try to replace /bin/sh with /bin/tcsh), and the two have mutually incomprehensible history schemes. For that matter, you may find that your history is not properly heritable between sessions, so the results of your program will not necessarily be the last few things you typed on the command line in your current interactive session.

    For this reason, if all you want is a command that shows you only the last few commands you typed, what you want is neither C nor shell scripts, but aliases. The only difference between bash and tcsh is that bash requires an '=' in the definition and tcsh requires none. To find out your current shell, type "echo ${SHELL}" at the command prompt.

    bash: alias shorthistory='history | tail -10'
    tcsh: alias shorthistory 'history | tail -10'

    Thereafter, every time you type 'shorthistory' you'll get what you expect. Substitute whatever command you like for "shorthistory," of course. You can persist this change by inserting the appropriate line above into the appropriate file below:

    bash: ~/.bash_login
    tcsh: ~/.login

    (where ~ indicates your home directory)

    Commands in these files are loaded when you open a new login shell, and will keep their effects even after a reboot.
     

Share This Page