I'm writing a program for a school project that is supposed to emulate the Unix shell, in a very basic form. It's basically parsing input, then doing a fork/exec. I need to be able to read arguments in the program (not as arguments passed to the program from the command line) individually. For example, I will prompt:
Please enter a command:
...and I need to be able to parse both...
ls
OR
ls -l
but the trouble is that there seems to be no easy way to do this. scanf() will pull each argument individually, but I see no way to place them into differing slots in a char* array. For example, if I do...
char * user_input[10];
for (int i=0; i<10; i++){
user_input[i] = (char *) malloc(100*sizeof(char));
}
for (int i=0; *(user_input[i]) != '#'; i++)
{
scanf("%s", user_input[index]);
index++;
}
...then user_input[0] will get "ls", then the loop will start over, then user_input[0] will get "-l".
gets and fgets just take the whole line. Obviously this problem can be logically solved by going through and plucking out each individual argument...but I'd like to avoid having to do that if there is an easy way that I'm missing. Is there?
Thanks!
If your use case is simple enough, you can do this with strtok:
char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);
The strtok() function parses a string into a sequence of tokens. On the first call to strtok() the string to be parsed should be specified in str. In each subsequent call that should parse the same string, str should be NULL.
You can use strtok or strtok_r to split the string on spaces.
If you're doing something more complex, where some of the arguments could have (quoted) spaces in them, you're pretty much stuck parsing it yourself - though you could have a look at the source of a shell (e.g. bash) to see how it handles it.
kilanash helpfully reminds me of my obvious omission - GNU getopt. You'll still have to have parsed into separate arguments yourself first, though.
Forget that scanf exists for it rarely does what you want. Get the whole line at once and then write code to split it up. strtok - the second most favored answer to this question - is also problem ridden.
You can use strtok_r to break the string up on whitespace. Note that it is a destructive operation (modifies the input string).
Try to see if anything of this will help you:
ANSI C Command Line Option Parsing Library
The Argtable Homepage
Regards,
Tiho
Related
I have been trying for hours to create a program that outputs something like command[/home/nerdofcode/]: by running something like:
printf("Command[", system("pwd"),"]: ");...
But the problem I am receiving is that when I go to type input, it starts typing at command[... and then once I click enter, it finally outputs the system("pwd");...
Technical Info
I am using the system() function to execute a system command on Linux.
To use printf correctly for a null-terminated string you need to change the parameters:
printf("Command[%s]: ", string_with_result);
In order to get the string_with_result correctly, you need to study the way system() works in your environment. Its return value is implementation specific and therefor does not allow to answer with code which does what you want.
char * string_with_result; /* pointer to null-terminated sequence of char */
This is the declaration for the string result to be used in printf as proposed above.
In case you want to just get the result, but do not insist on using system() check this StackOverflow question and accepted answer:
'pwd' to get path to the current file
This Q/A might be the way on a "unix-ish" environment to actually use system(), it uses popen():
C: Run a System Command and Get Output?
printf does not work as a "concat" operation of comma separated strings. It rather has a format string with placeholders and the arguments for the placeholders.
So you could write:
char *s1 = "hello", *s2 = "world";
printf("%s %s", s1, s2);
But the following code will not concat the both strings; it will rather treat s1 as a format string (without placeholders) and will ignore the argument:
char *s1 = "hello", *s2 = "world";
printf(s1, s2);
Note further that int system(const char* cmd) does not return a string, which you could use in printf then, at all. So I'd recommend to write.
printf("Command[");
system("pwd");
printf("]: ");
This should work on console as long as system-command and your programsstdout` target the same output stream.
My goal here is to read through a text file that has to follow these formatting regulations:
No spaces/tabs between characters
Characters must be either non-negative integer or new line character (no letters/symbols)
I can only use functions given in stdlib.h and stdio.h
I am thinking of reading through the file character by character using the fgetc() function, but I can't think of a way that tests whether or not the character is a new line character (isn't a new line char /n, which would be two chars together which would ruin the idea of going char by char?).
Following this train of thought I was thinking that using getline(), which would negate the necessity of checking if a char is a new line character, would be easier (am I right in thinking this or would this not negate such a requirement?). Yet if I were to do this what would be the easiest way to traverse through the char string that this would produce in order to still check each individual character?
Also, if someone could think of an easier route as to checking for the format of a file using the given libraries that would be much appreciated.
If you use getline it will retain the newline character so you'll still have to check for it.
If you can use any of the standard C library then you can use isdigit(...) from ctypes.h
It returns non-zero if the input character is a digit.
If you use the getline function the input would be written into the buffer that you pass as the first argument. It will append a null terminal character to this buffer so you can walk through it as so:
for(char* s = buffer; *s; s++)
/* test *s */
This piece of code is acting a bit strange to my taste. Please, anyone care to explain why? And how to force '\n' to be interpreted as a special char?
beco#raposa:~/tmp/user/foo/bar$ ./interpretastring.x "2nd\nstr"
1st
str
2nd\nstr
beco#raposa:~/tmp/user/foo/bar$ cat interpretastring.c
#include <stdio.h>
int main(int argc, char **argv)
{
char *s="1st\nstr";
printf("%s\n", s);
printf("%s\n", argv[1]);
return 0;
}
Bottom line, the intention is that the 2nd string to be printed in two lines, just like the first. This program is a simplification. The real program has problems reading from a file using fgets (not a S.O. argument to argv like here), but I think solving here will also solve there.
It seems the shell doesn't recognize and convert the "escape sequence". Use a shell software that supports \n escape sequence.
For all purposes, this just take care of \n and no other characters get special treatment.
This answer here does the job with lower complexity. It does not change "2 chars" into "one single special \n". It just changes <\><n> to "<space><newline>". That's fine. It would be better if there were a C Standard Library to interpret special chars in a string (as I know it has for RegExp for instance).
/* change '\\n' into ' \n' */
void changebarn(char *nt)
{
while(nt!=NULL)
if((nt=strchr(nt,'\\')))
if(*++nt=='n')
{
*nt='\n';
*(nt-1)=' ';
}
}
I'm trying to remove the extension of a file (I know it is .txt) using sscanf(). I've tried with many format strings I think may work, but with no success. The main problem is that I just can't understand sscanf()'s documentation, so I don't get how to use this [=%[*][width][modifiers]type=] I've tried to tell it that end must be ".txt" or to save initial string in a variable and a %4ccorresponding to the extension in another one, but again… can't make it work.
I know this has been asked before here: sscanf: get first and last token in a string but as I said... I don´t understand its solution.
The part of my code that does that:
sscanf(fileName,"the_sender_is_%s%*[.txt]", sender);
The input file name is, for example: "the_sender_is_Monika.txt"
In sender I should have
Monika
but whatever I try gives me
Monika.txt
When you use
sscanf(fileName,"the_sender_is_%s%*[.txt]", sender);
The function reads as much as it can with %s before it processes %*[.txt].
Use
sscanf(fileName,"the_sender_is_%[^.]", sender);
While sscanf() is powerful, it is not the universal tool. There are limits on what you can do with it, and you're hitting them. A moderate approximation to the task would be:
char body[32];
char tail[5];
if (sscanf("longish-name-without-dots.txt", "%31[^.]%4s", body, tail) != 2)
…oops — can't happen with the constant string, but maybe with a variable one…
This gets you longish-name-without-dots into body and .txt into tail. But it won't work all that well if there are dots in the name part before the extension.
You're probably looking for:
const char *file = "longish-name.with.dots-before.txt";
char *dot = strrchr(file, '.');
if (dot == NULL)
…oops — can't happen with the literal, but maybe with a variable…
strcpy(tail, dot); // Beware buffer overflow
memcpy(body, file, dot - file);
body[dot - file] = '\0';
I am originally a Java programmer who is now struggling with C and specifically C's pointers.
The idea on my mind is to receive a string, from the user, on a command line, into a character pointer. I then want to access its individual elements. The idea is later to devise a function that will reverse the elements' order. (I want to work with anagrams in texts.)
My code is
#include <stdio.h>
char *string;
int main(void)
{
printf("Enter a string: ");
scanf("%s\n",string);
putchar(*string);
int i;
for (i=0; i<3;i++)
{
string--;
}
putchar(*string);
}
(Sorry, Code marking doesn't work).
What I am trying to do is to have a first shot at accessing individual elements. If the string is "Santillana" and the pointer is set at the very beginning (after scanf()), the content *string ought to be an S. If unbeknownst to me the pointer should happen to be set at the '\0' after scanf(), backing up a few steps (string-- repeated) ought to produce something in the way of a character with *string. Both these putchar()'s, though, produce a Segmentation fault.
I am doing something fundamentally wrong and something fundamental has escaped me. I would be eternally grateful for any advice about my shortcomings, most of all of any tips of books/resources where these particular problems are illuminated. Two thick C books and the reference manual have proved useless as far as this.
You haven't allocated space for the string. You'll need something like:
char string[1024];
You also should not be decrementing the variable string. If it is an array, you can't do that.
You could simply do:
putchar(string[i]);
Or you can use a pointer (to the proposed array):
char *str = string;
for (i = 0; i < 3; i++)
str++;
putchar(*str);
But you could shorten that loop to:
str += 3;
or simply write:
putchar(*(str+3));
Etc.
You should check that scanf() is successful. You should limit the size of the input string to avoid buffer (stack) overflows:
if (scanf("%1023s", string) != 1)
...something went wrong — probably EOF without any data...
Note that %s skips leading white space, and then reads characters up to the next white space (a simple definition of 'word'). Adding the newline to the format string makes little difference. You could consider "%1023[^\n]\n" instead; that looks for up to 1023 non-newlines followed by a newline.
You should start off avoiding global variables. Sometimes, they're necessary, but not in this example.
On a side note, using scanf(3) is bad practice. You may want to look into fgets(3) or similar functions that avoid common pitfalls that are associated with scanf(3).