Redirect command output to file in C - c

I am trying to redirect output of ls command to a file fout.txt
But file fout.txt contains only
Here is the output
Below is my program:
int fout, ferr;
char *tok[3];
tok[0] = "ls", tok[1] = ">", tok[2] = "fout.txt";
fout = open("fout.txt", O_RDWR|O_CREAT|O_APPEND, 0600);
ferr = open("ferr.txt", O_RDWR|O_CREAT|O_APPEND, 0600);
puts("Here is the output\n");
execvp(tok[0], tok);
close(fout);
close(ferr);
Please help here.

Redirection such as you are attempting is not done directly via execvp() call. It's done in bash like this:
/* Execute a simple command that is hopefully defined in a disk file
somewhere.
1) fork ()
2) connect pipes
3) look up the command
4) do redirections
5) execve ()
6) If the execve failed, see if the file has executable mode set.
If so, and it isn't a directory, then execute its contents as
a shell script.
Note that the filename hashing stuff has to take place up here,
in the parent. This is probably why the Bourne style shells
don't handle it, since that would require them to go through
this gnarly hair, for no good reason.
Note the "connect pipes".
(This is not meant to be a complete answer - the questioner should be required to research something...)

The different redirections characters (< > >> |)are interpreted by the shells. As you ask a direct execvp execution, you just execute ls command with parameters > and fout.txt.
When I do that I get on stderr:
ls: >: No such file or directory
ls: fout.txt: No such file or directory
To redirect with the exec family of function you must explicitely open the files and use dup2 to connect them to stdin (0), stdout(1) and/or stderr(2) as shown in In C how do you redirect stdin/stdout/stderr to files when making an execvp() or similar call?

Related

Is there a way to redirect stderr to a file that works in bash, csh and dash?

How do I redirect stderr (or stdout+stderr) to a file if I don't know which shell (bash, csh, dash) is interpreting my command?
My C code running on Linux/FreeBSD/OSX needs to call an external program via the system() function, which will use /bin/sh to interpret the supplied command line. I would like to capture the messages printed by that external program to stderr and save them to a file. The problem is that on different systems /bin/sh points to different shells that have different syntax for redirecting the stderr stream to a file.
The closest thing I found is that bash actually understands the csh-style syntax for redirecting stderr+stdout to a file:
some_program >& output.txt
but dash, which is the default shell on Ubuntu (i.e. very common), does not understand this syntax.
Is there a syntax for stderr redirection that would be correctly interpreted by all common shells? Alternatively, is there a way to tell system() (or some other similar C function?) to use /usr/bin/env bash instead of /bin/sh to interpret the supplied command line?
You have a mistaken assumption, that /bin/sh can be an "alternate" shell like csh that's incompatible with the standard shell syntax. If you had a system setup like that, it would be unusably broken; no shell scripts would work. Pretty much all modern systems attempt to conform, at least superficially, to the POSIX standard, where the sh command processes the Shell Command Language specified in POSIX, which is roughly equivalent to the historical Bourne shell and which bash, dash, ash, etc. (shells which are commonly installed as /bin/sh) are all 99.9% compatible with.
You can completely ignore csh and similar. They're never installed as sh, and only folks who actually want to use them, or who get stuck using them as their interactive shell because some evil sysadmin setup the login shell defaults that way, ever have to care about them.
On any POSIX-like system, you can use
system("some_program > output.txt 2>&1");
This is because POSIX system is equivalent to calling sh, and POSIX sh supports this kind of redirection. This works independently of whether or not a user opening a terminal on the system will see a Csh prompt.
How do I redirect stderr (or stdout+stderr) to a file if I don't know which shell (bash, csh, dash) is interpreting my command?
You don't. Bourne-family shells and csh-family shells have different, incompatible syntax for redirecting stderr. In fact, csh and tcsh do not have a syntax to redirect only stderr at all -- they can redirect it only together with stdout.
If you really could be in any shell at all, then you're pretty much hosed with respect to doing much of anything. One could imagine an obscure, esoteric shell with completely incompatible syntax. For that matter, even an unusual configuration of a standard shell could trip you up -- for example if the IFS variable is set to an unusual value in a Bourne-family shell, then you'll have trouble executing any commands that don't take that into account.
If you can count on executing at least simple commands, then you could execute a known shell within the unknown one to process your command, but that oughtn't to be necessary for the case that seems to interest you.
Alternatively, is there a way to tell system() (or some other similar
C function?) to use /usr/bin/env bash instead of /bin/sh to interpret
the supplied command line?
Not on a POSIX-conforming system. POSIX specifies explicitly that the system() function executes the command by use of /bin/sh -c [the_command]. But this shouldn't be something to worry about, as /bin/sh should be a conforming POSIX shell, or at least pretty close to one. Definitely it should be a Bourne-family shell, which both bash and dash are, but tcsh most definitely is not.
The way to redirect the standard error stream in a POSIX shell is to use the 2> redirection operator (which is a special case of a more general redirection feature applicable to any file descriptor). Whatever shell /bin/sh actually is should recognize that syntax, and in particular bash and dash both do:
some_program 2> output.txt
I think, there is another possibility worth mentioning: You could open the file you want to redirect on stderr in your c-code prior to calling system(). You can dup() the original stderr first, and then restore it again.
fflush(stderr); // Flush pending output
int saved_stderr = dup(fileno(stderr));
int fd = open("output.txt", O_RDWR|O_CREAT|O_TRUNC, 0600);
dup2(fd, fileno(stderr));
close(fd);
system("some_program");
dup2(saved_stderr, fileno(stderr));
close(saved_stderr);
This should perform the output redirection as you need it.
If you don't know the shell.... of course you don't know how to redirect from it, despite of the fact that you can see what value the $SHELL has, and act in consequence:
char *shell = getenv("SHELL");
if (*shell) { /* no SHELL variable defined */
/* ... */
} else if (!strcmp(shell, "/bin/sh")) { /* bourne shell */
/* ... */
} /* ... more shells */
Despite of what you say in your question, it is quite unusual to rename /bin/sh to use another shell, as shell scripts use syntax that depends on that. The only case I know is with bash(1), and I have seen this only in Linux (and remarkably, last versions of solaris), but the syntax of bash(1) is a superset of the syntax of sh(1), making it possible to run shell scripts made for sh(1) with it. Renaming /bin/sh to perl for example, would make your system probably completely unusable, as many system tools depend of /bin/sh to be a bourne compatible shell.
By the way, the system(3) library function always calls sh(1) as the command interpreter, so there should be no problem to use it, but there's no solution to capture the output and process it by the parent process (indeed, the parent process is the sh(1) that system(3) fork(2)s)
Another thing you can do is to popen(3) a process. This call gives you a FILE pointer to a pipe of a process. You popen its input in case you popen(3) it for writing, and you popen its output if you want or read its output. Look at the manual for details, as I don't know now if it redirects only its standard output or it also redirects the standard error (I think only redirects standard output, for reasons discussed below, and only if you popen(3) it with a "r" flag).
FILE *f_in = popen("ps aux", "r");
/* read standard output of 'ps aux' command. */
pclose(f_in); /* closes the descriptor and waits for the child to finish */
Another thing you can do is to redirect yourself after fork(2)ing the child, and before the exec(2) call (this way you can decide if you want only stdout or if you want also stderr redirected back to you):
int fd[2];
int res = pipe(fd);
if (res < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
if ((res = fork()) < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (res == 0) { /* child process */
dup2(fd[1], 1); /* redirect pipe to stdout */
dup2(fd[1], 2); /* redirect pipe also to stderr */
close(fd[1]); close(fd[0]); /* we don't need these */
execvp(program, argv);
perror("execvp");
exit(EXIT_FAILURE);
} else { /* parent process */
close(fd[1]); /* we are not going to write in the pipe */
FILE *f_in = fdopen(fd[0]);
/* read standard output and standard error from program from f_in FILE descriptor */
fclose(f_in);
wait(NULL); /* wait for child to finish */
}
You can see a complete example of this (not reading standard error, but it is easy to add --- you have only to add the second dup2() call from above) here. The program executes repeatedly a command you pass to it on the command line. It needs to get access to the output of the subprocess to count the lines, as between invocations, the program goes up as many lines as the program output, to make the next invocation to overlap the output of the last invocation. You can try it and play, making modifications as you like.
NOTE
In your sample redirection, when you use >&, you need to add a number after the ampersand, to indicate which descriptor you are dup()ing. As the number before the > is optional, the one after the & is mandatory. So, if you have not used it, prepare to receive an error (which probably you don't see if you are redirecting stderr) The idea of having two separate output descriptors is to allow you to redirect stdout and at the same time, conserve a channel where to put error messages.

exec function only running some commands, won't run echo

I'm trying to run command line arguments (specifically echo) through the exec family of functions. I can get the execv function to run if I write my own executable and run it, but if I try to run touch or echo it returns -1
#include <stdio.h>
#include <unistd.h> // exec functions
#include <sys/types.h> // pid_t
#include <sys/wait.h>
#define HIGH 1
#define LOW 0
int digitalWrite(int pin, short type) {
pid_t pid = fork();
if (pid == 0) {
printf("pid == %i\n", pid);
if (type == HIGH) {
char* args[] = {"echo", "1", ">", "/sys/class/gpio/gpio67/value", NULL};
int val = execv(args[0], args);
printf("ran function execl, %i\n", val);
} else {
printf("Unable to do anything but set pin to HIGH\n");
}
} else if (pid < 0) { // pid < 0
printf("fork failed\n");
}
wait(NULL);
}
int main() {
printf("Starting digitalWrite\n");
digitalWrite(0, HIGH);
printf("Completed digitalWrite()\n");
return 0;
}
Just for context here's my build:
$ gcc wiringbeagle.c
$ ./a.out
Starting digitalWrite
pid == 0
ran function execl, -1
Completed digitalWrite()
Completed digitalWrite()
$ ls
a.out wiringbeagle.c
The command echo 1 > /sys/class/gpio/gpio67/value runs fine in the terminal on it's own, and if I create a local file (i.e. touch tmpfile.txt) and try to run echo hi > tmpfile.txt it runs as expected in my command line but doesn't run in the program.
I must be not understanding something with execv, and any assistance would be greatly appreciated!
The first argument to execv is the file to be executed. Unlike your shell, execv does not search through the directories indicated by the PATH environment variable, so you need to give it the complete path to the executable. Unless there is an executable file called echo in your current working directory, execv("echo",...) will fail with a "file not found" error. (Use perror to get better error messages).
If you want to search for the executable as the shell does, use execvp. But note that your shell probably executes echo as a built-in command, so it won't be the same echo as your shell uses. In this case, that's fine.
Once you fix that, you will encounter a different problem. Since you are just invoking a command-line utility with arguments, rather than using a shell, the argument ">" is just an argument. It is the shell which handles redirections (as well as pipes, quoting, and a bunch of other useful stuff). So all you will accomplish is to send the three arguments to stdout.
You could use the system function to execute a command using the shell, or you could set up the redirection yourself by freopening stdout in your child before doing the execvp.
You can get quite a lot of information about system interfaces using the man command. For example, to learn what freopen does, use man freopen. You can also read manpages on the internet, eg. http://man7.org/linux/man-pages/man3/freopen.3.html, but the documentation on your own system is right there, and also applies to the actual version of the software installed on your system (assuming you installed the documentation).
I'm not entirely certain why you're even using the exec family to run external programs in this case. The C standard library provides perfectly adequate file I/O stuff.
For example, you can simply fopen, fprintf, and fclose the file without ever starting another external process to do that work for you:
int bytesWrit = 0;
FILE *gpioHndl = fopen("/sys/class/gpio/gpio67/value");
if (gpioHndl != NULL) {
bytesWrit = fprintf(gpioHndl, "1\n");
fclose(gpioHndl);
}
if (bytesWrit != 2) {
HandleError();
}
This is probably the preferred way to do what you want, which is simply writing a fixed value to a file.
In terms of why your execv call isn't working (though it's totally irrelevant if you take my advice above), there are several things you need to be aware of.
First, while some commands are actually files on the disk that you can exec, others may be internal bash commands(a). On my system, for example:
pax:~$ type ftp
ftp is /usr/bin/ftp
pax:~$ type echo
echo is a shell builtin
One way to solve this is to run the actual bash executable (which, being an on-disk command, can be done via exec), telling it to run its internal echo command. From the command line:
pax:~$ bash -c 'echo xyzzy'
xyzzy
Second, if want to use redirection, this is normally something that's done by the shell, not the exec calls or individual executables.
Trying to do redirection via the exec family will generally only result in the >somefile being passed as a literal parameter to the executable (in the argv array), not being used to attach standard output to a file. In other words, it won't work unless the executable specifically handles redirection, which is rare.
So that means you will have to run the shell with redirection and have it run the executable after performing those redirections, even if the command is not an internal one.
Thirdly, if you want the path searched for your executable, execvp is the call you want, not execv (the latter just uses the file you explicitly provide, either relative from the current working directory or an absolute path like /bin/ls). So, in your case, you should either:
use execvp to search the path; or
fully specify the path with execv.
(a) The echo command, while it is bash-internal may also be provided as a separate executable (I believe Posix requires this), so this may not be an issue here. It may be an issue if you expect them to act exactly the same in terms of more esoteric arguments :-)
execv() does not search the PATH environment variable in order to find an executable file. Per the Linux execv() man page (bolded text added):
...
Special semantics for execlp(), execvp(), and execvpe()
The execlp(), execvp(), and execvpe() functions duplicate the actions
of the shell in searching for an executable file if the specified
filename does not contain a slash (/) character. ...
...
So, those three will search the PATH environment variable if the filename passed does not contain a / character.
You're using execv(), which is not one of those three. Therefore, execv() will not search the PATH environment variable.
Since your current working directory doesn't contain an executable file called echo, execv() fails.
You need to use execvp() per the man page.
You need to use absolute path as first parameter in execv
Then, the correct is:
char* args[] = {"/bin/echo","echo", "1", ">", "/sys/class/gpio/gpio67/value", NULL};
But to run what you want (put value '1' in file '/sys/class/gpio/gpio67/value'), you need to use command sh:
char* args[] = {"/bin/sh", "sh","-c", "/bin/echo 1 > /sys/class/gpio/gpio67/value", NULL};
The parameter to "sh -c" is a string. Then, you need to put all command together as a string

C - Redirecting IO of Child Process

I am trying to redirect the IO of a child process (after fork()) into a file, and I can't figure out why it isn't working.
Here's what I've done:
if(fork() == 0){
execv(exe, (char*[]){ exe, "> temp.exe" });
...
And the executable runs, but it doesn't redirect to the file. I would appreciate it if anyone could explain what am I doing wrong, and how I should do it. I'm getting a feeling I need to redirect before the execv() but I have no idea how to.
Thanks in advance!
Shell redirections (like > file) are implemented by the shell. By using execve(), you are bypassing the shell; the child process will see "> temp.exe" in argv, and will attempt to process it as an argument.
If you want to redirect output to a file, the easiest approach will be to implement that redirection yourself by opening the file after forking and using dup2() to move its file descriptor to standard output:
if (fork() == 0) {
int fd = open("temp.exe", O_CREAT | O_WRONLY, 0666);
if (fd < 0) { handle error... exit(255); }
dup2(fd, 1);
close(fd);
execv(exe, ...);
}
The execX() family of calls does not have the same flexibility as, say system() or popen(). These latter methods call shell to do the interpretation of the command.
The arguments to the execX call are the exact path of the program you want to run and the arguments you want to give to that program. Any "shell" features such as redirection you have to implement yourself before calling execX.
Alternatively, you can let shell actually do the work, execp("sh","sh",myexe+" >test.txt");, but that is lazy, and then why not just use system anyway?
Two very useful methods are pipe() and dup2(): pipe allows you to create pipes to your host program; dup2 lets you set a scenario where the program being executed thinks that it is writing to stdout (1), or reading from stdin (0), but is actually writing or reading to a file or pipe that you created.
You will get a long way by reading the man pages for pipe and dup2, or in google looking for exec pipe and dup2, so I won't take your enjoyment away by writing a full implementation here.

execlp command doesn't take into account the asterisk wildcard

this little command :
execlp("/bin/echo", "echo", "*", ">", "toto", 0)
prints * > toto in the terminal, but I want it to print the result of echo * in the file toto.
The command : system("echo * > toto")  works well, but I want to use the execlp command, what I am doing wrong?
Thank you in advance.
The angle bracket ('>') redirection is shell specific.
You could do, for example:
execlp("/bin/sh", "/bin/sh", "-c", "/bin/echo * > toto", NULL);
Note that this invokes 2 specific behaviors that are shell related:
* wildcard: the asterisk wildcard will be expanded (by the shell, very important) to all files in current directory; and
> redirection: the stdout of the echo command will be redirected to file (or pipe) toto.
If you want to do the same kind of redirection in C (i.e. without resorting to executing the shell) you must:
// open the file
int fd = open("toto", "w");
// reassign your file descriptor to stdout (file descriptor 1):
dup2(fd, 1); // this will first close file descriptor, if already open
// optionally close the original file descriptor (as it were duplicated in fd 1 and is not needed anymore):
close(fd);
// finally substitute the running image for another one:
execlp("/bin/echo", "echo", "*" 0);
Note that you'll still get '*' written to the file.
Edit: the first argument to execlp is really the executable to run, file image that will substitute the currently running process. After this first argument comes the full argv array, which must include argv[0]. I've edited the code above to reflect this. Some programs use this argv[0] to change its personality (for example, busybox is a single executable that implements ls, echo, cat and many other unix command line utilities); that surely is the case with bash and whatever is linked from /bin/sh.

UNIX run program within another program

I am trying to execute a program from within a C program (inside UNIX).
I have been given an executable ( the program requires a string input during execution and writes that input to another file called sample ) called exec and I want to execute it in program.c, but giving the string input through indirection.
For that I created a file as follows:
% vim input
I wrote the following inside the input file
content
Now in program.c,
#include<unistd.h>
int main()
{
const char* command = "./exec < input";
execvp(command, NULL);
return 0;
}
When I run the program, the content is not entered into the sample file.
But when I run it without indirection, i.e.
const char* command = "./exec";
then it works, and input entered in saved in sample file.
Can someone please tell what am I doing wrong in the indirection syntax.
Thanks.
The syntax you are using is supposed to be interpreted by a shell like bash, csh, ksh, etc.
The system call execvp only expects the path to the executable and a number of arguments, the shell is not invoked there.
To perform redirection in this manner, you'll have to use the dup2(2) system call before calling execvp:
int fd = open("input", O_RDONLY);
/* redirect standard input to the opened file */
dup2(fd, 0);
execvp("/path/to/exec", ...);
Of course, you'll need some additional error checking in a real-world program.
You can't do redirection like that with execvp. Use system() or start getting friendly with dup() and friends. You might google 'implementing redirection'.. you'll likely turn up plenty of examples of how shells (for example) handle this problem.
The exec(3) family of functions does not know anything about input redirection or parsing command lines: it tries to execute exactly the executable you give it. It's trying to search for an executable file with the name "./exec < input", which unsurprisingly does not exist.
One solution would be to use the system(3) function instead of exec. system invokes the user's shell (such as /bin/bash), which is capable of parsing the command line and doing appropriate redirections. But, system() is not as versatile as exec, so it may or may not be suitable for your needs.
The better solution is to do the input redirection yourself. What you need to do us use open(3) to open the file and dup2(3) to duplicate the file descriptor onto file descriptor 0 (standard input), and then exec the executable.

Resources