I'm new to C and I'm trying to use an if statement to check for an argument (in this case "jobs") but it doesn't seem to be working...
int builtin_cmd(char **argv)
{
printf("test1\n");
if (!strcmp(argv[0], "quit")) { //quit command
exit(0);
}
if ((!strcmp(argv[0], "fg")) || (!strcmp(argv[0], "bg"))) { //fg or bg command
do_bgfg(argv);
return 1;
}
if (!strcmp(argv[0], "jobs")) { //jobs command
printf("test2\n");
listjobs(jobs);
printf("test3\n");
return 1;
}
printf("test4\n");
return 0; /* not a builtin command */
}
I input "jobs", but based on the test output (1-4-repeat), it's not registering. Does anyone know what might be going wrong?
argv[0] points to the program name, not the first argument. argv[1] points to that, if the argument is present.
C Standard, § 5.1.2.2.1, Program Startup:
If the value of argc is greater than zero, the string pointed to by argv[0] represents the program name; argv[0][0] shall be the null character if the program name is not available from the host environment. If the value of argc is greater than one, the strings pointed to by argv[1] through argv[argc-1] represent the program parameters.
Emphasis mine.
Related
in the following program:
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
char *delivery = "";
int thick = 0;
int count = 0;
char ch;
for (int i = 0; i < argc; i++) {
fprintf(stdout, "Argv[%i] = %s\n", i, argv[i]); /* printing to understand (ptu) */
}
while ((ch = getopt(argc, argv, "d:t")) != -1)
switch (ch) {
case 'd':
fprintf(stdout, "Optind in case 'd': %i\n", optind);
delivery = optarg;
break;
case 't':
fprintf(stdout, "Optind in case 't': %i\n", optind);
thick = 1;
break;
default:
fprintf(stderr, "Unknown option: '%s'\n", optarg);
return 1;
}
fprintf(stdout, "Argc: %i\n", argc); /* ptu */
fprintf(stdout, "Argv: %p\n", argv); /* ptu */
argc -= optind;
argv += optind;
fprintf(stdout, "Optind: %i. Argc after subtraction: %i, Argv after increment: %p\n", optind, argc, argv);
if (thick)
fprintf(stdout, "Thick crust!\n");
if (delivery[0])
fprintf(stdout, "To be delivered %s\n", delivery);
fprintf(stdout, "Ingredients:\n");
for (count = 0; count < argc; count++)
fprintf(stdout, "%s\n", argv[count]);
return 0;
}
When I run the above program with arguments shown below I get the following output:
[u#h c]$ ./prog -t -d yesterday anchovies goatcheese pepperoni
Argv[0] = ./prog
Argv[1] = -t
Argv[2] = -d
Argv[3] = yesterday
Argv[4] = anchovies
Argv[5] = goatcheese
Argv[6] = pepperoni
Optind in case 't': 2
Optind in case 'd': 4
Argc: 7
Argv: 0x7ffebee8e498
Optind: 4. Argc after subtraction: 3, Argv index: 0x7ffebee8e4b8
Thick crust!
To be delivered yesterday
Ingredients:
anchovies
goatcheese
pepperoni
I would like to know if my understanding of what's going on under the hood is accurate, specifically for the argument parsing steps in the program. Apologies for not sharing a more minimal reprex, but in this case I probably couldn't. I wouldn't have spammed stackoverflow if I could show this to a friend who understood C. So, please bear with me. Here goes nothing:
Defined main to accept command-line (cl) parameters. This requires two parameters:
integer argc which will contain the number of cl parameters including the name of the program, in this case it is 7
array of strings (i.e. array of char pointers), each element of which will point to the memory address of the first element of each string literal (stored in the CONSTANT memory block) passed in as a cl parameter to the program.
for loop (self explanatory)
On each run of the while loop getopt() will parse the argv[] array and assigns the next matching character from the optstring "d:t" to character variable ch, until it runs out of options (no pun intended) which is when it will return -1 and the control will exit the while loop.
at each such pass optind (which is initiated at 1 presumably because argv[0] is the program name) will be incremented to contain the index of the next element to be processed in argv... So in case 't', optind = <index of "-d" i.e. 2>, and in case 'd', optind = <index of "anchovies" i.e. 4> (this is because getopt() realizes from the ":" after 'd' in the optstring that -d will be followed by its optarg on the command line, thus optind is incremented to "4" here instead of being "3")
after -t and -d yesterday are processed getopt() can't find anything else in argv[] which matches elements in the optstring; thus it returns -1 and we break out of the while loop. optind remains set to 4 because getopt didn't find anything else after '-d' from the optstring.
We now decrement optind's value "4" from argc to ensure we skip past the option arguments (which we have already parsed) to the remaining three non-option arguments. We also increment argv—which initially pointed to memory location of argv[0] i.e. "./prog"—by <optind * sizeof(char pointer on a 64-bit machine); i.e. 4 * 8> which is why argv now points 32 bytes ahead in memory: 0x7ffebee8e4b8 - 0x7ffebee8e498 == 0x20. In other words, argv[0] points to "anchovies"
We then print stuff depending on values of thick, delivery and loop through the remaining non-option arguments to print them as well...
Yes, your understanding is correct.
I would offer two notes:
"String literal" refers to a string that is actually defined within your program, as characters between "" delimiters. The "CONSTANT block" isn't a standard concept but I suppose you mean a block of read-only memory which is loaded from the binary, since that's where string literals normally reside. The strings which the argv pointers point to are not of this kind; they can't be, because they are not known when the binary is created. Instead, they are located in some unspecified region of memory, and you may modify them in place if you wish (though this is likely to make your code confusing); e.g. argv[0][3] = 'x' would be legal. (C17 standard 5.1.2.2.1 (2)).
Some people might likewise find it confusing to modify the values of argc and argv within main, and would suggest that you assign the modified values to some other variables instead:
int remaining_argc = argc - optind;
char **remaining_argv = argv + optind;
This question already has answers here:
How to check if a string is a number?
(16 answers)
Closed 2 years ago.
I'm currently learning C and I'm messing around with command line inputs from the user.
I understand that argc is the count, i.e. the amount of entered commands, and argv is an array of what was entered.
I know how to check to see if the user entered a value after the called program with argc > 1 etc, and I know that argv takes string type variables which I can convert to real integers using atoi(). However I'm not sure how to implicitly only accepts integers. I've tried using isdigit etc around argv but I usually end up with a segment error. Is there a way around this?
int main(int argc, string argv[])
{
if (argc == 2 && //code to ensure argv[1] is int)
{
//code
}
}
"...not sure how to implicitly only accepts integers."
This is not implicitly done, you have to explicitly test each string for its contents.
Using functions in the string test category, for example isdigit, walk through each of the argv string arguments, i.e from argv[1] through argv[argc - 1] and test the character type to ensure it is a numeric value, from 0 to 9 and -. (Note: other valid numeric char symbols can included, eg: e, x,et.al. but are not in this limited example.)
A simple example:
given command line: prog.exe 23 45 1f4 -57
int main(int argc, char *argv[])
{
for(int i=1;i<argc;i++)
{
if(!test_argv(argv[i]))
{
//notify user of wrong input, and exit
printf("argv[%d] contains non numeric value. Exiting\n", i);
}
}
//continue with program
return 0;
}
bool test_argv(const char *buf)
{
while(*buf)
{
if((!isdigit(*buf)) && (*buf != '-'))
{
return false;
}
buf++;
}
return true;
}
Results in:
This question already has answers here:
How to properly compare command-line arguments?
(4 answers)
Closed 4 years ago.
So I have to write a program that contains two possible options depending on what the user chooses by entering either -i or -w. I'm very new to command line arguments in general and I have no idea how to do this. So far I have:
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc == -'i') {
puts("Test 1");
}
else if(argc == -'w') {
puts("Test 2");
}
return 0;
}
It doesn't print anything...
Any explanation is greatly appreciated. I'm just trying to understand the logic behind this.
First of all, you are comparing oranges with appels. argc stores the number of
arguments. Second, even if you used argv[1], the comparison would be still
wrong, because it would be comparing pointers, not the contents. Note that in C
a string is a sequence of characters that terminates to '\0'-terminating byte.
A char* variable only points to the start of that sequence. The == operator
checks for equality of values, in case of pointers (and string literals), it
compares whether both pointers point to the same location.
When you want to compare strings, you have to compare the strings themselves,
that means you have to compare the contents where the pointers are pointing to.
For strings you have to use the strcmp function like this:
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 2)
{
fprintf(stderr, "usage: %s option\n", argv[0]);
return 1;
}
if(strcmp(argv[1], "-i") == 0)
puts("Test 1");
else if(strcmp(argv[1], "-w") == 0)
puts("Test 2");
else {
fprintf(stderr, "Invalid option '%s'\n", argv[1]);
return 1;
}
return 0;
}
Note that it is important to check first that you have enough command line
arguments (the first if of my code). If the user didn't pass any argument,
argc will be 1 and argv[1] will point to NULL, which would yield undefined
behaviour if it is passed to strcmp. That's why I only do strcmp when I'm
100% sure that argv[1] is not NULL.
Also if you are coding for a POSIX system (linux, mac, etc), then I recommend using
getopt for parsing of the command line arguments.
You have to check argv[i] where i is the array number of the command line argument being put in, argv[0] would be the file name called upon and after that argv[1] would be the first statement, argv[2] the next and so on
argc means "argument count". meaning the number of arguments
argv is a 2-dimensional array. Strings are 1-dimensional character arrays in C. The second dimension comes from you having multiple String.
So if you want the first String argument, you would access it as follows:
argv[0]
You are also attempting to compare strings, which are more than 1 character long. You should use strcmp to compare strings in C. See How do I properly compare strings?
and if you want to compare equality, you would not use ==, == is for basic data types such as int or char.
argc represents the number of parameters that were passed in at the command line including the program name itself.
In c, a character, e.g., 'i' is an 8-bit number representing the ASCII code of the letter i. So your conditional statement if(argc == -'i') is actually checking whether -105 (105 is the ascii value of the letter i) is the number of arguments that was passed to your program.
To check whether the arguments passed in are "-i" or "-w" you need to perform string comparison, using the c library functions or your own algorithm, and you need to be comparing those strings to argv[i] (where i is the position of the parameter you're checking in the program invocation)
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("The name of the program is %s\n", argv[0]);
if( strcmp(argv[1], "-i") == 0 ) { puts("Test 1"); }
if( strcmp(argv[1], "-w") == 0 ) { puts("Test 2"); }
return 0;
}
I want to read argument and then compare it with a character:
int main (int argc, char *const argv[]) {
if (argv[1][1] == 'c') {
client();
}
else if (argv[1][1] == 's') {
server();
}
return 0;
}
It works if I type cc or dd, it also works if I type ccttttt. It's just taking the second character, but I'd like it to work only if I type c.
Just change argv[1][1] to argv[1][0] and remember in c/c++ all arrays start in '0'
argv[1] is a pointer to the first command-line argument.
argv[1][1] is the second character of that argument.
The first character is argv[1][0]. (But first check that argc >= 2, i.e., that there actually is a command-line argument.)
Of course this only checks a single character, so it doesn't distinguish between "c" and "cthulhu". If that's how you want to handle the argument, that's fine, but you might want to consider a different approach.
I'm getting a segfault thrown when using invalid input or the -help flag in the command arguments. It is a re-creation of the Unix expand utility, and its supposed to handle errors in a similar fashion.
int main(int argc, char *argv[]){
char help1[]= "-help";
char help2[]= "--help";
int spaces; //number of spaces to replace tabs
if (argc==1){ //if only one argument in stack
//check if asking for help
if ( (strcmp(argv[1], help1)==0) || (strcmp(argv[1], help2)==0) )
printHelp();
else
printError(); //otherwise, print error message
//right number of tokens are provided, need to validate them
} else if (argc>=2){
spaces= atoi(argv[2]); //assign it to spaces
parse_file(spaces); //open the stream and pass on
}
return 0;
}
My printerror method:
void printError(){
fprintf(stderr, "\nInvalid Input.\n");
fprintf(stderr, "The proper format is myexpand -[OPTION] [NUMBER OF SPACES]\n");
exit(1);
}
When I try invalid input or the help flag, I get a segfault. Why is this, since I'm checking if the first flag is help?
If a single command-line parameter is passed to your program, argc == 2, so you need to replace
if (argc==1){ //if only one argument in stack
with
if (argc==2){
Note that in most systems argv[0] is the program name and in this case argc is at least 1. You can think of argc as the number of elements in argv. If you’re testing for argv[1], you’re expecting argv to have at least two elements (argv[0] and argv[1]), hence argc needs to be at least 2.
argv[0] counts too, so if argc==1 argv[1] is NULL
Your help message should be displayed if there are less than 2 parameter given, hence
if (argc<3)
printHelp();
else if(...)
Upon initialization, the arguments to main will meet the following requirements according to this.
argc is greater than zero.
argv[argc] is a null pointer.
argv[0] through to argv[argc-1] are pointers to strings whose meaning will be determined by the program.
argv[0] will be a string containing the program's name or a null string if that is not available. Remaining elements of argv represent the arguments supplied to the program. In cases where there is only support for single-case characters, the contents of these strings will be supplied to the program in lower-case.
As such, you are passing in argv[argc] (which is a null pointer) to strcmp.