This question already has an answer here:
Is it possible to include a shell script in a C program
(1 answer)
Closed 6 years ago.
How can I execute a command with bash? system() uses sh and not bash.
I know that I can execute commands in bash with system("/bin/bash -c command"). But I have a very long command and /bin/bash -c gives me problems. What I need is bashrun(command) or something else.
The command is a string, not a file
Case1 : script from a file - Use the shebang
#!/usr/bin/env bash
at the top of your script and then do
int status=system("/full/path/to/script");
if(status==-1){
// failure mode
}
Case2 : script stored as a string
Do something like below
char *command="$(which bash) -c 'ls'";
int status=system(command);
if (status==-1){
//failure mode
}
If you're creating a very long shell command and you need bash to interpret it, then you have two real options:
Save the text into a file and invoke bash with the filename as a single argument (equivalently, use a shebang in the file to specify bash as the interpreter, make the file executable, and invoke that as command), or
Start an instance of bash with popen() and write the shell command as standard input to the bash process.
If you're having problems due to shell script quoting (rather than the command length), then either of those options would work, or you could implement the equivalent of system() but using execl() to pass the argument without going through sh. I'm assuming a POSIX-type system here.
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int run_bash(const char *command)
{
int pid = fork();
if (pid < 0) {
/* failed */
perror("fork");
return pid;
} else if (pid == 0) {
/* child */
execl("/bin/bash", "bash", "-c", command, (char*)NULL);
perror("exec");
exit(EXIT_FAILURE);
} else {
/* parent */
int status;
do {
waitpid(pid, &status, 0);
} while (!WIFEXITED(status));
return WEXITSTATUS(status);
}
}
int main(void)
{
run_bash("echo '*'");
}
Related
How can I execute the following command using execvp, if it's possible:
"ls | tee ~/outputfile.txt"
I've tried to run the following code but got this message: execvp() not expected: No such file or directory
I'm not sure for the cause of this issue,
I can't execute this command because this is concatenation command ?
#include <unistd.h> // execvp()
#include <stdio.h> // perror()
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
int main(void) {
char *const cmd[] = {"ls | tee ~/outputfile.txt", NULL};
execvp(cmd[0], cmd);
perror("Return from execvp() not expected");
exit(EXIT_FAILURE);
}
In the bottom line, want to write the output of the command 'ls' to a file in my code.
Thank you in advance!
You can't use execvp (or any exec* function family) like that.
First of all, the first argument must be the path to the executable file.
I doubt you have an 'ls | tee ~/outputfile.txt' executable somewhere on your computer.
You have 'ls' or 'tee' probably, but not 'ls | tee ~/outputfile.txt'.
Secondly : exec* function family can't do nativly piping (the '|' part) : you have to do it yourself.
An example is the following :
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *const cmd[] = {"ls", "/home", NULL};
execvp(cmd[0], cmd);
perror("Return from execvp() not expected");
exit(EXIT_FAILURE);
}
That will do a ls in "/home".
It's up to you to pipe it in another execve : this can greatly help you.
If you just want to execute your command line wthout any regard for security, you can use "system"
I encounter a question while I am reading a textbook - Unix System Programming
How big is the argument array passed as the second argument to execvp
when you execute execcmd of Program 3.5 with the following command
line?
execcmd ls -l *.c
Answer: The answer depends on the number of .c files in the current
directory because the shell expands *.c before passing the command
line to execcmd.
Program 3.5:
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "restart.h"
int main(int argc, char *argv[]) {
pid_t childpid;
if (argc < 2){ /* check for valid number of command-line arguments */
fprintf (stderr, "Usage: %s command arg1 arg2 ...\n", argv[0]);
return 1;
}
childpid = fork();
if (childpid == -1) {
perror("Failed to fork");
return 1;
}
if (childpid == 0) {
execvp(argv[1], &argv[1]);
perror("Child failed to execvp the command");
return 1;
}
if (childpid != r_wait(NULL)) {
perror("Parent failed to wait");
return 1;
}
return 0;
}
Why is the size of argument array passed depending on the number of .c files in the current directory? Isn't it the argument array just something like
argv[0] = "execcmd";
argv[1] = "ls";
argv[2] = "-l";
argv[3] = "*.c";
argv[4] = NULL;
Update: Find a link explains pretty well about the shell expansion. May useful for someone who see this post later also do not understand shell expansion.
Description about shell expansion
No, because the shell does the wild card expansion.
It finds files in the directory that match the expression, so for instance you can use "echo *.c" to discover what the shell would match. Then it lists out, every filename matching *.c on the exec call or if none *.c which is likely to result in an error message about file not found.
It is more powerful that the shell does the expansion, the same file wildcarding is immediately available for all programs, like cat, echo, ls, cc.
This question already has an answer here:
How to run bash with root rights in C program?
(1 answer)
Closed 7 years ago.
I need to write a program in C which opens bash shell as root. I could not find a function which would be do that. I try something like that:
system("bash");
but i don't know what next
By default, your program will open a shell and run other programs as the same user which itself is running as. That is, if you run your program from a root account, it will execute other programs as root. Otherwise you can try this:
system("echo \"password\" | sudo -S bash"); # note the different quotes
But keep in mind that hard coding your password is highly inadvisable.
Since you asked how to do it in C, here's an idea how you could do it on Linux:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main( void )
{
int pid = fork();
if ( pid == 0 )
{
# first argument: command to start ("sudo")
# second argument: program name for sudo ("$0" in shell)
# third argument: first argument to sudo, name of the command to execute as root
execlp( "sudo", "sudo", "bash", NULL );
}
int status;
// wait for bash to finish
wait( &status );
return 0;
}
Tested on Ubuntu 15.04.
I was working on my project when I needed to use "curl" to obtain some data from www. Now firstly I tried direct system() function but it didn't worked, strangely everytime it corrupted the whole source code file while compiling with gcc. Luckily I was testing it separately.
Then I tested execl() function, this code compiles OK and gcc gives me a .exe file to run, but nothing happens when I run it,blank windows appears. CODE:
int main(){
execl("curl","curl","http://livechat.rediff.com/sports/score/score.txt",">blahblah.txt",NULL);
getch();
return 0;
}
Includes are not shown properly but I have included stdio,conio,stdlib and unistd.h.
How can I get output of program to store in text file? Also running the above command creates and stores text file in My Documents, I want it to be in local directory from where I run the program. How can I do that?
You need to provide the path of curl, and you cannot use redirection because the application will not be executed through bash. Instead use the -o flag and specify the filename. Also, execl does not return when successful:
#include <unistd.h>
#include <stdio.h>
int main(){
execl("/usr/bin/curl",
"curl","http://livechat.rediff.com/sports/score/score.txt",
"-oblahblah.txt",NULL
);
printf("error\n");
return 0;
}
If you want your code to return, you should fork a child process to run the command. This way you can check the return code.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define CURL "/usr/bin/curl"
int main()
{
pid_t pid;
int status;
pid = fork();
if (pid == 0)
{
execl(CURL, CURL, arg1, NULL);
}
else if (pid < 0)
{
printf("Fork failed\n");
exit (1);
}
else
{
if (waitpid(pid, &status, 0) != pid)
status = -1;
}
return status;
}
arg1 is whatever argument you want to use with curl or if you aren't using any than you obviously can omit it.
I'm supposed to create a linux shell using C. Below is my code:
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SHELL "/bin/sh"
#include "extern.h"
int mysystem (char *command)
{
int status;
pid_t pid;
pid = fork ();
if (pid == 0)
{
execl (SHELL, SHELL, "-c", command, NULL);
_exit (EXIT_FAILURE);
}
else if (pid < 0)
status = -1;
else
if (waitpid (pid, &status, 0) != pid)
status = -1;
return status;
}
Everything is right when I test the code using different commands like "ls", "man", etc. but when I use notepad to create a testfile containing the following:
echo "hello"
exit 2
the return code come out to be 512 when it's supposed to be just 2.
Can anyone help me fix my code?
status is not the exit code; it contains other information as well. Normally the return value is in bits 8-15 of status, but you should be using the macros in wait.h to extract the return value from status in a portable way.
Note that 512 is 2<<8.
Make sure you're using the macros like WIFEXITED and WEXITSTATUS on your status value. See your operating system's man page for waitpid. Here is a description of the POSIX requirements on waitpid.
By notepad do you mean you're using a Windows program to create a Unix shell script? That doesn't work because you end up with CRLF at the end of each line instead of LF. Try the "dos2unix" command on the script to convert it to Unix format and then run it.
I assume you're aware that code is already available in the system() library call? Judging by your function name, I'd guess you're just trying to learn how to do it with system calls.
Try enclosing your command string you supply to /bin/sh with quotes, because otherwise the space character makes /bin/sh think you are supplying another option to the shell itself, not to the command you are calling. For example, try this in a terminal:
/bin/sh -c exit 2
echo $?
and
/bin/sh -c "exit 2"
echo $?
The first one gives 0, and the second one gives the desired 2.