Mac calling "system" command from C


macrumors newbie
Original poster
Jun 13, 2006
hi all,

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



char s[100];
int a = 10;
sprintf(s, "history | tail -%d", a);
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.



Moderator emeritus
Jul 24, 2002
It something to do with the command (my guess is the pipe).

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

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

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

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

which history
cannot find it so it's probably a shell built-in.


macrumors 6502a
Jun 17, 2003
robbieduncan said:
which history
cannot find it so it's probably a shell built-in.
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:
sprintf(s, "tail -%d ~/.bash_history", a);


macrumors 6502a
Jun 17, 2003
pilotError said:
Try using "bash history" also

You need to exec the shell in order to run shell commands.
"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:

sprintf(s, "echo \"history\" | bash -i | tail -%d", a);
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:

"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".


macrumors 68000
Mar 6, 2003
Toronto, Canada
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?


macrumors 68020
Sep 18, 2001
Denver, CO
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.