Why does my bash script not work in my C program? - c

My program takes an arbitrary number of words from the user and stores them in a double pointer **stringArr. These values are then concatenated into a string which is then passed into a bash script I have.
The problem I have is that the bash script doesn't echo the command I want it to, and I am unsure why.
string = malloc(N * sizeof(char));
for (int j = 0; j < N; j++) {
strcat(string, stringArr[j]);
strcat(string, " ");
}
puts("\n\nYour input sorted in alphabetical order:");
if (fork() == 0) {
execl("./sortString.sh", "sortString.sh", string, NULL);
}
#!/bin/bash
for NAME in "$#"
do
VAR=$VAR" "$NAME
done
echo $VAR | tr ’ ’ ’\n’ | sort | tr ’\n’ ’ ’
Is there something I am missing?
Note: the bash script is in the same directory as the program; the program works in regards to taking user input and it putting into the string string.
If you want to try out the bash script, an example of string I have passed through is: "one two three " (there is a space after 'three').

You cannot use the exec() family of functions to execute a shell script directly; they require the name of a proper executable. If you check the return value of your execl() call, I'm sure you'll see an error (-1), and errno will probably have been set to ENOEXEC.
You could try using system() instead, but it is generally frowned upon because you'd need to build a full (single) command string, and making sure that everything is properly escaped and such is error-prone.
Instead, I'd recommend that you give "/bin/sh" or "/bin/bash" as the first argument to exec(). Then, the args to sh would need to be the path of your script, and then the args that your script will use.
(this is what your shell does automatically when you run a script; it reads the #! line, and executes "/bin/bash your-script your-args...")
Your string allocation is too small. You're allocating space for N chars, but you're strcating N spaces into it, plus whatever is in your stringArr (which I assume is not full of empty strings).
Even then, you will have just one big string of args, but exec() wants them separated. Think of it like if you point quotes around all the args in bash; you get just one big argument, which contains spaces.
After you fix that (so that you have an array of strings rather than just one big one), you will run into problems with execl(). It takes the arguments separately, but you're trying to send one big array of them. You'll want execv() instead, which accepts an array of strings for the argument list. Remember that the first string in that list must be your script path.
Are you sure this wouldn't be easier to do with qsort()?

Related

Open file from command line and display what was typed

I'm trying to input only one filename from command line. Then I want to use an If statement to compare the filename to 4 different names I'm expecting to see. If I don't get one of the 4 expected file names then I need to print it back to the user with what was inputted and then exit the program safely.
int main(int argc, char *argv[])
{
....
}
I've been trying a lot of different methods of getting this done, but I just can't figure it out. I was thinking maybe the way I take the input argument is wrong. Any help would be greatly appreciated.
-edit
I just want to be clear I don't want you to be a leech and solve the question I have for me. Just at least point me in the correct direction. I can't figure how to make a for loop work with the filename.
for(argv == "UnexpectedFile.csv"){
printf("this is an unexpected file: %c", argv[1]);
}
You could use strcmp() from string.h to compare strings like
strcmp(argv[1], "unexpectedfile.csv");
It returns 0 when the strings are equal.
If you have the 4 file names in as an array of strings, say expectedFile, do
for(i=0; i<4 && strcmp(argv[1], expectedFile[i])!=0; ++i);
If the value of i is the total number of file names (ie, 4) after this loop, argv[1] is an unexpected file.
Otherwise, value of i would be the index of the file name string in the expectedFile array.
The command line arguments are stored in the 2-dimensional char array argv.
argv[0] would be the name of the executed file. The arguments you give start only from argv[1] onwards.
argc will have the total number of command line arguments including the file name stored in argc. So if there are 'no' arguments, argc would be 1.
In your case the file name is the only argument, so argc would be 2.
You must check if argc is at least 2 before you access argv[1] to prevent the program from accessing argv[1] when it isn't there.
(You do not ask for complete code solution (and do not provide enough of your code for that). So here are the pointers in the right direction you want.)
The comparison you do with a simple pointer == pointer does not really compare the content of the strings. That is what e.g. strcmp () is for, as proposed by #user3629249.
And in order to print out what was given as commandline argument, you should use "%s\n".
And in order to set up a for loop, you will have to do the syntax right: for(init assignment; condition; step operation).
(If you need more help, you will have to provide more details on what behaviour you get and what you do not like about it. Currently your code looks more like compiler errors, which you did not quote, than a problem with the behaviour goal...)

Process command line argument on windows

I need a hand in terms of processing command line argument (on Windows) in C.
Suppose I have the following situation
C:\Users\USER\Desktop> my_executable arg1 ' "A>200 && B<300 (just some conditions" '
In this case argc = 5
and
C:\Users\USER\Desktop> my_executable arg1 '"A>200 && B<300 (just some conditions"'
In this case argc = 3
Depending on users, the argv and argc will be different.
How can I write the code such that the condition and arg1 can be stored correctly :)
Required:
arg1 is stored into a char pointer
condition is also stored into a char pointer
Thanks
Don't use single quotes as argument quotes on Windows unless you want to implement your own argument parser. ^ can be used to escape " and itself and a few other things. To embed " in arguments use "".
If you really need to, call GetCommandLineW and parse yourself. GetCommandLineW returns a string that consists of the executable image name possibly enclosed in double quotes, followed by an optional space and the arguments exactly as given to CreateProcess (which means that ^ processing has already taken place).

Reading arguments for execv from stdin? (C)

I have a method execfile which takes the path of an executable file and then executes it, I'm now trying to add the option for the user to enter arguments to the command being executed. I currently have it implemented like so:
int execfile(char *file) {
printf("Enter any arguments to %s: ", file);
char *arg = (char *) calloc(ARG_MAX, sizeof(char));
scanf("%s", arg);
execl(file, file, arg, NULL);
return -1;
}
This does function crudely in that I can execute /usr/bin/ps and enter el or -el at the argument prompt and the function will execute with both arguments as intended.
Ideally though it would be more elegant to be able to enter arguments as you traditionally would when executing a C program directly, so say enter -e -l at the prompt and have it correctly interpreted by the program (currently this wont work), or just immediately press enter to skip the prompt (currently have to enter at least some character).
I thought the best way to do this would be to declare an array char *argv[], set arg[0] = file then scan the rest of the input and place each separate argument in an array cell, then calling execv(file, argv).
I'm very new to C however and am unsure of how this can be implemented in the most efficient way, for example I was considering reading in the whole string first then using a loop to iterate through it character by character to recognise arguments to add to argv, but it seems a bit longwinded? Is there no way to read these arguments into the array directly from stdin?
Additionally I'm unsure what to set ARG_MAX to as I don't know what the limit on number of arguments is within C, and I don't know how to get it to recognise that the enter key has been pressed so to skip immediately.
Is there no way to read these arguments into the array directly from stdin?
In one go, without knowing how many whitespace delimited argument will be passed, and without additional parsing?
No.

How to interpret special characters in command line argument in C?

First problem:
Suppose we write a simple program which takes in command line arguments and prints to a file. If the user enters
writetofile Hello!0\n w%orl\t!##y
bash replies with
!0: event not
found.
Without the user knowing things like using quotes ('') or escape characters ('\'), how do I handle this stuff instead of bash understanding it as a command?
Second problem:
Once I get these arguments, how do I interpret them as special characters and not sequences of characters. (ie. \t is tab, not '\''t')
That is, how do make sure that the program writes this to the file:
Hello!0
w%orl !##y
and not
Hello!0\n w%orl\t!##y
You can't get bash to stop parsing a command line as a command line. In your particular case, set +H would disable history expansion, thus allowing !0 as a literal, but a command would still fail if you included (parenthesis), $variables, #comments, ampersand & and a dozen other elements of shell syntax.
Note that this only applies to the parsing stage of the shell, not for any data that is passed literally into execve or similar.
Either make the user quote the data ('single quotes' will take care of everything except other single quotes), or make your program read the data itself so that the interaction goes
$ writetofile
Please enter your gibberish and press enter: Hello!0\n w%orl\t!##y
Thank you for your eloquent contribution.
$
Regarding the second problem: as #Jims said, you could use printf(1) to print the string. Good idea, but be aware that that would work only because the escapes you (appear to) want to recognise, like \t and \n, are the same as the ones that printf recognises.
These escapes are common, but there's nothing fundamental about them, so the general answer to your question – and the answer if you want to recognise an escape that printf doesn't – is for your program to interpret them. The C-like code to do that would be something like:
char *arg = "foo\\tbar\\n"; /* the doubled backslashes are to make this valid C */
int arglen = strlen(arg);
for (i=0; i<arglen; i++) {
if (arg[i] == '\\') { // we've spotted an escape
// interpret a backslash escape
i++; // we should check for EOS here...
switch (arg[i]) {
case 'n': putchar('\n'); break;
case 't': putchar('\t'); break;
// etc
}
} else {
// ordinary character: just output it
putchar(arg[i]);
}
}
(insert error handling and good style, to taste).
First problem: Use single quotes. ie: writetofile 'Hello!0\n w%orl\t!##y'
Second problem: Use printf.

parsing space separated text in C

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

Resources