Part Ⅱ–execute commands
Exec
This brings us to the exec
family of functions. Namely, it has the following functions:
execl
execv
execle
execve
execlp
execvp
For our needs,we will use execvp
whose signature looks like this
int execvp(const char *file, char *const argv[]);
execvp function indicates that,it accepts the name of a file,for which it will search for $PATH
variable of the system and an array of arguments to be executed.
A few things to note about the execvp
function:
- The first argument is the name of the command
- The second argument consists of the name of the command and the arguments passed to the command itself. It must also be terminated by
NULL
. - It also swaps out the current process image with that of the command being executed, but more on that later.
execvp.c
#include <unistd.h>
int main() {
char *argv[] = {"ls","-l","-h","-a",NULL};
execvp(argv[0],argv);
return 0;
}
If you compile and execute the execvp.c, you will see an output similar to the following:
total 32K
drwxrwxr-x 2 marco marco 4.0K 3月 1 22:07 .
drwxrwxr-x 5 marco marco 4.0K 3月 1 22:04 ..
-rwxrwxr-x 1 marco marco 17K 3月 1 22:07 execvp
-rw-rw-r-- 1 marco marco 123 3月 1 22:07 execvp.c
Which is exactly the same if you manually executels -l -h -a
readline
https://tiswww.case.edu/php/chet/readline/rltop.html
With execvp function,we can perform commands in $PATH
but how to accept the string of commands as stdin?
This brings us to the Readline Library.
#include <stdio.h>
#include <readline/readline.h>
char * readline (const char *prompt);
However,when I used this function,error occured.
Ubuntu that I used doesn’t have the library by default.Hence,the library need to be installed.
sudo apt-get install libreadline-dev
Meanwhile,we should add an another argument to link the library when compiling the File.c
,otherwise you may see an error like
“undefined reference to `readline’
collect2: error: ld returned 1 exit status”.
gcc File.c -o File -lreadline
And here’s the code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <readline/readline.h>
#include <sys/wait.h>
char **get_input(char *);
int main() {
//in C,ptr can be used as array.
char **command;
char *input;
pid_t child_pid;
int stat_loc;
while(1) {
input = readline("mysh> ");//with "mysh> " to be a prompt,the function reads a line of input.
command = get_input(input);
if(!command[0]) {//empty commands
free(input);
free(command);
continue;
}
child_pid = fork();
if(child_pid == 0) {
//child process
execvp(command[0],command);
} else {
waitpid(child_pid, &stat_loc, WUNTRACED);
}
free(input);
free(command);
}
return 0;
}
char **get_input(char *input) {
char **command = malloc(8 * sizeof(char *));
char *separator = " ";
char *parsed;
int index = 0;
parsed = strtok(input, separator);
while (parsed != NULL) {
command[index] = parsed;
index++;
parsed = strtok(NULL, separator);
}
command[index] = NULL;
return command;
}
Let’s test it.
However,the exec
family of function can’t perform built-in commands like cd
.
The code with weak robustness should be revised to be more robust and stronger.
-
Dynamic memory allocation - in
char ** get_input(char *input);
The command variable only malloc 8 sizeof(char *).It’s limited,
so you will see the following error:
To handle the error,command
should malloc dynamic memories.
-
fork failed - If the OS runs out of memory or reaches the maxmum number of allowed processes,a child process will not be created.We add the following segment to our code:
if(child_pid < 0) { perror(command[0]); exit(1); }
-
exev failed - the exev function may fail.We modify the following block to our code:
//child process if (execvp(command[0], command) < 0) { perror(command[0]); exit(1); }
The revised code is here,written by chatGPT.The AI is amazing!!!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <readline/readline.h>
#include <sys/wait.h>
char **split_input(char *);
int main() {
//in C,ptr can be used as array.
char **command;
char *input;
pid_t child_pid;
int stat_loc;
while(1) {
input = readline("mysh> ");//with "mysh> " to be a prompt,the function reads a line of input.
command = split_input(input);
if(!command[0]) {//empty commands
free(input);
free(command);
continue;
}
//fork failed.
child_pid = fork();
if(child_pid < 0) {
perror(command[0]);
exit(1);
}
if(child_pid == 0) {
//child process
if (execvp(command[0], command) < 0) {
perror(command[0]);
exit(1);
}
} else {
waitpid(child_pid, &stat_loc, WUNTRACED);
}
free(input);
free(command);
}
return 0;
}
char **split_input(char *input) {
int capacity = 10;
char **command = malloc(capacity * sizeof(char *));
if (command == NULL) {
fprintf(stderr, "Error: memory allocation failed\n");
exit(EXIT_FAILURE);
}
char *token = strtok(input, " ");
int i = 0;
while (token != NULL) {
if (i == capacity - 1) { // resize the array if it is full
capacity *= 2;
char **new_command = realloc(command, capacity * sizeof(char *));
if (new_command == NULL) {
fprintf(stderr, "Error: memory allocation failed\n");
exit(EXIT_FAILURE);
}
command = new_command;
}
command[i] = malloc((strlen(token) + 1) * sizeof(char));
if (command[i] == NULL) {
fprintf(stderr, "Error: memory allocation failed\n");
exit(EXIT_FAILURE);
}
strcpy(command[i], token);
token = strtok(NULL, " ");
i++;
}
command[i] = NULL; // terminate the array with NULL
return command;
}