I have been trying to make some kind of "my own shell". So, what I have been trying to do is get input with fgets() and execute it with execvp().
If I use execvp with an array made by me, it works as expected. However, if I try to do it with the results of fgets then I get no output.
main() {
char str[64];
char *array[sizeof(str)];
char *p = NULL;
int i = 0;
printf("my_shell >");
fgets(str, sizeof(str), stdin); // Use fgets instead of gets.
p = strtok(str," ");
while (p != NULL) {
array[i++] = p;
p = strtok(NULL, " ");
}
execvp(str, array);
}
As commented by user3386109, the solution was:
First, the array must have a NULL pointer at the end. Second, the delimiter string passed to both strtok should be " \n" (that's a space followed by a newline). You need the newline because fgets will put a newline character into your buffer, and you don't want that newline added to the array as an argument. Finally, put a perror("execvp failed"); after the execvp so that you get some indication of the problem when the execvp fails.
Related
Hi i'm struggling with understanding what's wrong with my program.
My best guess is something related with this line of code here:
scanf("%s", str);
The thing is i'm trying to call a function that uses a strtok on a String passed on to it typed by the user, all of this inside of a while loop as shown in the code example below:
int i = 0;
char str[80];
while(i != 3){
printf("Type in some string so i can break it: ");
scanf("%s", str);
testFunc(str);
printf("Loop %i ended.\n", i);
i++;
}
return 1;
Result (not what i want, see further below what i actually want):
Type in some string so i can break it: hey there how are you doing!
hey
Loop 0 ended.
Type in some string so i can break it:
there
Loop 1 ended.
Type in some string so i can break it:
how
Loop 2 ended.
The reason why i think this is caused by the scanf line is because the program works fine when i'm using instead some dummy pre-declared String
int i = 0;
while(i != 3){
char str[80] = "hey there how are you doing!";
testFunc(str);
printf("Loop %i ended.\n", i);
i++;
}
return 1;
Result:
hey
there
how
are
you
doing!
Loop 0 ended.
hey
there
how
are
you
doing!
Loop 1 ended.
hey
there
how
are
you
doing!
Loop 2 ended.
Here's the funtion that uses strtok, most of the code here is taken from https://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm
int testFunc(char linha[80]){
//
const char s[2] = " ";
char *token;
/* get the first token */
token = strtok(linha, s);
/* walk through other tokens */
while(token != NULL) {
printf("%s\n", token);
token = strtok(NULL, s);
}
return 1;
}
I'm puzzled, it's like the program is executing testFunc() in paralel with the main function.
I think the problem is when you execute this loop:
while(i != 3){
printf("Type in some string so i can break it: ");
scanf("%s", str);
testFunc(str);
printf("Loop %i ended.\n", i);
i++;
}
scanf gets only one word at a time, so the loop cycles 3 times, you only get 3 words no matter how long the input string is.
On the other hand, in your other example you already have a string to break apart so the function will work.
There are different ways to get spaced strings from the console but here is what I consider to be a good option to do it:
str[MAX_SIZE];
fgets(str, MAX_SIZE, stdin);
// where MAX_SIZE is the maximum size you want to allow for the string,
//must be smaller than str.
According to scanf(3) man page:
Matches a sequence of non-white-space characters; the next
pointer must be a pointer to the initial element of a
character array that is long enough to hold the input sequence
and the terminating null byte ('\0'), which is added
automatically. The input string stops at white space or at
the maximum field width, whichever occurs first
You can use fgets or fread for input:
char buffer[1000];
/* fgets (reads a line of text with trailing newline */
fgets (buffer, 1000, stdin);
I am writing a simple command line interpreter. My code reads a string using scanf and parses it using the function getArgs() shown below, and then uses that array as an argument to execvp to perform a command such as ls. It works if I call only 'ls' but when I call 'ls -la', it gives the same result as 'ls'.
void getArgs(char* command, char* args[]){
int i = 0;
char* p = strtok(command, " ");
args[i] = p;
while(p != NULL){
i++;
p = strtok(NULL, " ");
args[i] = p;
}
}
Here is my main function which includes the initialization of the arguments given:
int main(){
char *args[1024];
char example[30];
char exit[5] = {'q', 'u', 'i', 't', '\0'};
int f1;
int status;
size_t n = sizeof(args)/sizeof(args[0]);
while(strncmp(example, exit, 30) !=0){
printf(">>>");
scanf("%s", example);
getArgs(example, args);
int x = strncmp(args[0], exit, 30);
if (x != 0){
f1 = fork();
if (f1 != 0){
/* wait for child process to terminate */
waitpid(f1, &status, 0);
}
else{myExec(args);}}
else{
return 0;}}
return 0;
}
My guess as to the problem is that my argument array, args, is not null terminated and so when I attempt to use it in myExec():
void myExec(char* args[]){
execvp(args[0], args);
}
this does not work. So my question is, can I set the item after the last non-empty part of my array to null to try to get this to work? If so, how can I do that? Is there a better way to solve this?
The -la is being ignored because scanf("%s", example); will stop at the first space. I suggest
scanf(" %29[^\n]", example);
Which will
Ignore whitespace left in the buffer from the previous command.
Restrict the string input from overflowing.
Allow space seperators in the command.
Note too that in the first execution of while(strncmp(example, exit, 30) !=0) the example is an uninitialised variable, so that needs to be
char example[30] = "";
The %s directive stops scanning at the first whitespace character, so it won't properly capture any commands with spaces (such as ls -la). You should use fgets to get user input if you want to preserve any whitespace:
if ( fgets( example, sizeof example, stdin ) )
{
getArgs( example, args);
...
}
fgets will read up to sizeof example - 1 characters into example (including the newline!) and 0-terminate the string. You may want to take that newline into account with your strtok call.
I am work in parsing the commands that we get from stdin. My code nearly works. It prints all tokens except the first token. below is my code
/* Read a command line */
if (!fgets(line, 1024, stdin))
return 0;
char *p = strtok (line, " \n");
while (p != NULL)
{
Array[tokenscounter++] = p;
p = strtok (NULL, " \n");
}
}
return 0;
}
when i print all tokens stored in an array, it does not print the first one. any reason why is it behaving like that?
I have use strtok to assign data to a variable, cmd, from a user input. How can i detect if cmd is empty? Similarly to checking if cmd is q as seen below:
void readcmd() {
char read_input[50];
char* cmd;
char* param;
scanf("%[^\n]%*c",read_input);
cmd = strtok(read_input, " ");
param = strtok(NULL, " ");
if (strcmp(cmd, "q") == 0) {
printf("quitting\n");
exit(0);
}
run(cmd, param);
}
The strtok() function will return NULL if no tokens can be obtained from the input string.
I'm assuming that you want to know whether there's any point in calling strtok() at all, in other words does the buffer contain any data for tokenising?
You could set a nul (\0) byte in the first character of the string:
read_input[0] = '\0';
Then call scanf() - after scanf() returns you can check to see if the first byte of the buffer is still a nul byte:
if (read_input[0] != '\0') {
/* call strtok() */
} else {
/* buffer is empty. */
}
The \0 won't cause any problems when you pass it to say, printf() or strtok(), you could still use printf() to output the empty string since the \0 byte simply tells functions like strtok() and printf() that they've reached the end of a string.
Example, you can hand-pack a string:
read_input[0] = 'a';
read_input[1] = 'b';
read_input[2] = 'c';
read_input[3] = '\0';
printf("%s", read_input);
if(strcmp(cmd,"")==0){
...
}
I'm writing a shell and I'm using getline() with stdin from the keyboard to take commands. I'm having trouble tokenizing the inputs though. I tried using \n as a delimiter in the strtok() function, but it seems not to be working.
For example, I included an if statement to check if the user typed "exit" in which case it will terminate the program. It's not terminating.
Here's the code I'm using:
void main() {
int ShInUse = 1;
char *UserCommand; // This holds the input
int combytes = 100;
UserCommand = (char *) malloc (combytes);
char *tok;
while (ShInUse == 1) {
printf("GASh: "); // print prompt
getline(&UserCommand, &combytes, stdin);
tok = strtok(UserCommand, "\n");
printf("%s\n", tok);
if(tok == "exit") {
ShInUse = 0;
printf("Exiting.\n");
exit(0);
}
}
if (tok == "exit")
tok and exit are pointers, so you are comparing two pointers. This leads to an undefined behavior, since they don't belong to the same aggregate.
This is not the way to compare strings. Use rather strcmp.
if (strcmp (tok, "exit") == 0)
As #Kirilenko stated, you can't compare strings using the == operator.
But that's not it. If you're using getline() you don't need to split the input to lines anyway as getline() only reads a single line. And if you did want to split the input to other delimiters, you'd have call strtok() in a loop till it returns NULL.