How to run a program from a terminal (linux)?

Although it seems to me that I understand how fork() , exec() , wait() and pid work in C, I still have to find a way to start my personal program from the program.

Here is my code:

 #include<stdio.h> #include<stdlib.h> #include<unistd.h> /* for fork() */ #include<sys/types.h> /* for pid_t */ #include<sys/wait.h> /* fpr wait() */ int main(int argc, char* argv[]) { char fileName[255]; pid_t pid; switch (pid = fork()) { case -1: //Did not fork properly perror("fork"); break; case 0: //child execv(fileName[0],fileName); puts("Oh my. If this prints, execv() must have failed"); exit(EXIT_FAILURE); break; default: //parent //Infinite Loop while (1) { printf(" %s > ", argv[0]); scanf("%s", fileName); // gets filename if (fileName[0] == '\0') continue; printf("\n Entered file: %s",fileName); // prints the fileName waitpid(pid,0,0); /* wait for child to exit() */ break; } } return 0; } 

My questions are as follows:

  • I want to take a string as input, and I want to limit its scope to 255 characters. Is char fileName[255] and then scanf("%s", fileName); way to go Should I use getLine() or some other function instead?

  • Let's say that the input is accepted correctly. How can I say that there is an existing hello world program. Will the input be stored in *argv[] ? I found out that in another program I could use

     static char *argv[] = { "echo", "Foo is my name." , NULL }; execv("/bin/echo", argv); 

    to echo "Foo is my name." Can I do something similar with helloWorld?

+5
source share
2 answers

You pass one character as the command name, and the name string as the beginning of the argument list - as if the prototype for execv() was int execv(char cmd, char *args) .

Actual prototype: int execv(char *cmd, char **args) , so you need:

 char *args[2] = { fileName, 0 }; execv(args[0], args); 

I assume that you set fileName to a meaningful value somewhere - this is not shown. For example, it could be "./local_program" . It will be considered as the path to the executable file.

If you want to read the name, you can use fgets() or getline() , but you need to remove the new line:

 if (fgets(fileName, sizeof(fileName), stdin) != 0) { fileName[strcspn(fileName, "\n")] = '\0'; …as before… } 

or

 char *fileName = 0; size_t length = 0; if (getline(&fileName, &length, stdin) != -1) /* Not EOF! */ { fileName[strcspn(fileName, "\n")] = '\0'; …as before… } free(fileName); 

Using strcspn() avoids the special case of overly long command lines, so there is no new line in fileName . Note that this does not try to break the input string into the command name and arguments in spaces or something like that. This is the next level of shell implementation:

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(void) { char fileName[256]; if (fgets(fileName, sizeof(fileName), stdin) != 0) { fileName[strcspn(fileName, "\n")] = '\0'; char *args[129]; char **argv = args; char *cmd = fileName; const char *whisp = " \t\f\r\b\n"; /* Ugh — strtok()? OK while only handling white space separators */ char *token; while ((token = strtok(cmd, whisp)) != 0) { *argv++ = token; cmd = 0; } *argv = 0; execv(args[0], args); fprintf(stderr, "Oops!\n"); } return 1; } 

I do not need to check the args array overflow, since 256 input characters, minus the terminating zero, cannot be divided to create more than 128 single-character arguments, each of which is separated from the neighboring ones by a white space. Using strtok() is temporary tape help. As soon as you need to deal with the real shell syntax (pipes, I / O redirects, quoted strings with spaces, etc.), strtok() is a bad tool. (It is strtok() is also an incorrect function to use in any library function. Use POSIX strtok_r() on Unix or Microsoft strtok_s() on Windows if you must use strtok() -like in library code.)

+2
source

Accordingly, you need to fix compilation errors from your code:

 g++ -std=c++11 -g -Wall -Wextra -Wwrite-strings 36501711.cpp -o 36501711 36501711.cpp: In function 'int main(int, char**)': 36501711.cpp:18:25: error: invalid conversion from 'char' to 'const char*' [-fpermissive] execv(fileName[0],fileName); ^ 36501711.cpp:18:35: error: cannot convert 'char*' to 'char* const*' for argument '2' to 'int execv(const char*, char* const*)' execv(fileName[0],fileName); ^ 36501711.cpp: At global scope: 36501711.cpp:7:5: warning: unused parameter 'argc' [-Wunused-parameter] int main(int argc, char* argv[]) ^ 

The warning (about unused argc ) is harmless, but the errors are valid and need to be fixed.

You need to initialize fileName in front of you fork, otherwise the child will not have any valid data:

 #include<stdio.h> #include<stdlib.h> #include<unistd.h> /* for fork() */ #include<sys/types.h> /* for pid_t */ #include<sys/wait.h> /* fpr wait() */ int main(int argc, char* argv[]) { char fileName[255]; pid_t pid; //Infinite Loop while (1) { printf(" %s > ", argv[0]); scanf("%s", fileName); if (fileName[0] == '\0') break; printf("\n Entered file: %s\n", fileName); pid = fork(); switch (pid) { case -1: //Did not fork properly perror("fork"); break; case 0: //child execl(fileName, fileName, 0); perror("exec"); exit(EXIT_FAILURE); break; default: //parent waitpid(pid,0,0); /* wait for child to exit() */ break; } } return EXIT_SUCCESS; } 
0
source

Source: https://habr.com/ru/post/1246710/


All Articles