Handle command line arguments? [duplicate] - c

This question already has an answer here:
Pass File As Command Line Argument
(1 answer)
Closed 8 years ago.
So, I can make my program run and all, but if I'm given
$ ./a.out -f Text.txt
I'm just not sure how to get the program to make the connection that -f indicates a file. What is the logic for doing this?

The main function has signature int main(int argc, char**argv); so you can use the argc (which is positive) & argv arguments. The argv array is guaranteed to have argc+1 elements. The last is always NULL. The others are non-nil, non-aliased zero-byte terminated strings. Notice that often some shell is globbing the arguments before your program is started by execve(2): see glob(7).
For example, if you type (in a Linux terminal) myprog -i a*.c -o foo.txt and if at the moment you type that the shell has expanded (by globbing) a*.c into a1.c and a2.c (because these are the only files whose name start with a and have a .c suffix in the current directory), your myprog executable main program is called with
argc==6
argv[0] containing "myprog" (so you could test that strcmp(argv[0],"myprog") == 0)
argv[1] containing "-i"
argv[2] containing "a1.c"
argv[3] containing "a2.c"
argv[4] containing "-o"
argv[5] containing "foo.txt"
argv[6] being the NULL pointer
In addition you are guaranteed (by the kernel doing the execve(2)) that all the 6 argv pointers are distinct, non-aliasing, and non-overlapping.
GNU libc gives you several ways to parse these arguments: getopt & argp. getopt is standardized in POSIX (but GNU gives you also the very useful getopt_long(3))
I strongly suggest you to follow GNU conventions: accept at least --help and --version
The fact that e.g. -f is for some option and not a file name is often conventional (but see use of -- in program arguments). If you happen to really want a file named -f (which is a very bad idea), use ./-f
Several shells have autocompletion. You need to configure them for that (and you might configure them even for your own programs).

Related

Executable path as a variable parameter to exec

I've an application which needs to call a specific program 'mips64-unknown-linux-gcc' for linking all objects from a script with all required args for linking.
I am writing an exec function to call the compiler passed by script along with it's args. For this I wrote the code:
//prog.c : gcc prog.c -o prog
int main(int argc, char *argv[]) {
execvp("mips64-unknown-linux-gcc",argv);
}
This works, but the mips64-unknown-linux-gcc and argv are variables from script input.
I need execv first argument to be a variable which is compiler to be invoked. I can somehow (maybe) retrieve it by getenv(”CC”) but due to other dependencies my requirement is that exec shall accept the compiler and args at runtime (something like below). Is there any way I can do this?
./prog mips64-unknown-linux-gcc --sysroot=<<...>> -O3 -Wl -L <<...>> -L <<...>> -I <<...>> -L <<...>> abcd.o a1.o b2.o -o prog
I described my problem at my best. Please ask if anything is not clear.
From your example command line it seems that you want to take the first argument from command line as your command to execute and everything else should be passed to that command.
That is basically the same command line execpt for the first argument.
This makes things rather easy.
Looking at argv you will find these string:
char *argv[] = {"proc","mips64-unkown-linux-gcc", "--sysroot=<<...>>", ..., "-o", "prog", NULL};`
You can use that and call your command:
execvp(argv[1], argv+1);
Of course you should check whether you have at least one argument.
If you want do filter some options and handle in your own program instead of blindly passing it to execvp you must rebuild your own array of arguments where you do not include those options.

How is rustc able to compile source code from bash process substitution but gcc cannot?

$ rustc <(echo 'fn main(){ print!("Hello world!");}')
$ ls
63
$ gcc <(echo '#include<stdio.h> int main(){ printf("Hello world!\n"); return 0;}')
/dev/fd/63: file not recognized: Illegal seek
collect2: error: ld returned 1 exit status
Why can't ld link the program?
The gcc command is mostly a dispatch engine. For each input file, it determines what sort of file it is from the filename's extension, and then passes the file on to an appropriate processor. So .c files are compiled by the C compiler, .h files are assembled into precompiled headers, .go files are sent to the cgo compiler, and so on.
If the filename has no extension or the extension is not recognised, gcc assumes that it is some kind of object file which should participate in the final link step. These files are passed to the collect2 utility, which then invokes ld, possibly twice. This will be the case with process substitution, which produces filenames like /dev/fd/63, which do not include extensions.
ld does not rely on the filename to identify the object file format. It is generally built with several different object file recognisers, each of which depends on some kind of "magic number" (that is, a special pattern at or near the beginning of the file). It calls these recognisers one at a time until it finds one which is happy to interpret the file. If the file is not recognised as a binary format, ld assumes that it is a linker script (which is a plain text file) and attempts to parse it as such.
Naturally, between attempts ld needs to rewind the file, and since process substitution arranges for a pipe to be passed instead of a file, the seek will fail. (The same thing would happen if you attempted to pass the file through redirection of stdin to a pipe, which you can do: gcc will process stdin as a file if you specify - as a filename. But it insists that you tell it what kind of file it is. See below.)
Since ld can't rewind the file, it will fail after the file doesn't match its first guess. Hence the error message from ld, which is a bit misleading since you might think that the file has already been compiled and the subsequent failure was in the link step. That's not the case; because the filename had no extension, gcc skipped directly to the link phase and almost immediately failed.
In the case of process substitution, pipes, stdin, and badly-named files, you can still manually tell gcc what the file is. You do that with the -x option, which is documented in the GCC manual section on options controlling the kind of output (although in this case, the option actually controls the kind of input).
There are a number of answers to questions like this floating around the Internet, including various answers here on StackOverflow, which claim that GCC attempts to detect the language of input files. It does not do that, and it never has. (And I doubt that it ever will, since some of the languages it compiles are sufficiently similar to each other that accurate detection would be impossible.) The only component which does automatic detection is ld, and it only does that once GCC has irrevocably decided to treat the input file as an object file or linker script.
At least in your case, you can use process substition when specifying the input language manually, using -xc. However, you should put a newline after the include statement.
$ gcc -xc <(echo '#include<stdio.h>
int main(){ printf("Hello world!\n"); return 0;}')
$ ls
a.out
$ ./a.out
Hello world!
For a possible reason why this works, see Charles' answer and the comments on this answer.

How to pass run time arguments to a function in c through a shell script

I have a shell script which has to take arguments from the command line and pass it to a function in C. I tried to search but didn't find understandable solutions. Kindly help me out.
Should the arguments be passed via an option as a command in the shell script?
I have a main function like this:
int main(int argc, char *argv[])
{
if(argc>1)
{
if(!strcmp(argv[1], "ABC"))
{
}
else if(!strcmp(argv[1], "XYZ"))
{
}
}
}
How to pass the parameters ABC/XYZ from the command line through a shell script which in turn uses a makefile to compile the code?
You cannot meaningfully compare strings with == which is a pointer equality test. You could use strcmp as something like argc>1 && !strcmp(argv[1], "XYZ"). The arguments of main have certain properties, see here.
BTW, main's argc is at least 1. So your test argc==0 is never true. Generally argv[0] is the program name.
However, if you use GNU glibc (e.g. on Linux), it provides several ways for parsing program arguments.
There are conventions and habits regarding program arguments, and you'll better follow them. POSIX specifies getopt(3), but on GNU systems, getopt_long is even more handy.
Be also aware that globbing is done by the shell on Unix-like systems. See glob(7).
(On Windows, things are different, and the command line might be parsed by some startup routine à la crt0)
In practice, you'll better use some system functions for parsing program arguments. Some libraries provide a way for that, e.g. GTK has gtk_init_with_args. Otherwise, if you have it, use getopt_long ...
Look also, for inspiration, into the source code of some free software program. You'll find many of them on github or elsewhere.
How to pass the parameters ABC/XYZ from the command line through a shell script
If you compile your C++ program into an executable, e.g. /some/path/to/yourexecutable, you just have to run a command like
/some/path/to/yourexecutable ABC
and if the directory /some/path/to/ containing yourexecutable is in your PATH variable, you can simply run yourexecutable ABC. How to set that PATH variable (which you can query using echo $PATH in your Unix shell) is a different question (you could edit some shell startup file, perhaps your $HOME/.bashrc, with a source code editor such as GNU emacs, vim, gedit, etc...; you could run some export PATH=.... command with an appropriate, colon-separated, sequence of directories).
which in turn uses a makefile to compile the code?
Then you should look into that Makefile and you'll know what is the executable file.
You are using and coding on/for Linux, so you should read something about Linux programming (e.g. ALP or something newer; see also intro(2) & syscalls(2)...) and you need to understand more about operating systems (so read Operating Systems: Three Easy Pieces).
See following simple example:
$ cat foo.c
#include <stdio.h>
int main(int argc, char ** argv)
{
int i;
for (i = 0; i < argc; ++i) {
printf("[%d] %s\n", i, argv[i]);
}
return 0;
}
$ gcc foo.c
$ ./a.out foo bar
[0] ./a.out
[1] foo
[2] bar
$

Put operands first in getopt()

Using the getopt() function in C, it is possible to do that:
program -a arg_for_a -b arg_for_b -c operand1 operand2
and it works with no problem.
But, how to make it work this way?:
program operand1 operand2 -a arg_for_a -b arg_for_b -c
In this case, every argument, including the -a, -b etc. are considered to be operands.
I'm trying to make just like like gcc or ssh does:
gcc code.c -o executable
ssh user#host -i file.pem
That is, no matter in what position the options and the operands come, they are recognized properly.
How to make options be recognized properly wherever they are, and every word that do not follow an option be recognized an operand?
If you use the GNU C library's implementation of getopt, then it will work like all GNU utilities do, because almost all of them use it. In particular, (quoting from man 3 getopt):
By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end.
That's not quite the same as gcc. gcc cares about the relative order of optional and positional arguments. (For example, it makes a difference where -l goes in the command line.) To do that, you'll have to tell GNU getopt to not permute the arguments. Then, every time getopt reports that it's done, optind will have the index of the next positional argument (if any). You can then use that argument, increment optind, and continue using getopt, which will continue at the next argument.

Command Line Argument Counting

This is a simple C program that prints the number of command line argument passed to it:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("%d\n", argc);
}
When I give the input
file_name *
It prints 623 instead of 2 in my pc (operating system Windows 7). But it gives the correct output in other cases. Is * a reserved character for command line arguments?
Note this program gives correct output for the following input:
file_name *Rafi
Output = 2
On a Unix command line, the shell is responsible for handling wildcards. yourapp * will run yourapp, and pass the name of ALL of the non-hidden files in the current directory as arguments. In your case, that's 622 files (623 = 622 files + name of the program).
On Windows, applications are responsible for wildcard parsing, so argc is 2, 1 for the name of the program (argv[0]) and 1 for the wildcard (argv[1] = *);
That * gets expanded by the shell or the runtime library (the former on *nixes, the latter on Windowses), and instead of literal * you get the names of all the files in the current working directory.
As others have mentioned, you're getting the 'shell wildcard expansion' or 'globbing' where the * is used as a wildcard to match file names to place in the argv array.
On Unix systems this is performed by the shell and has nothing (or little) to do with the C runtime.
On Windows systems, this functionality is not performed by the shell (unless possibly if you're using some Unix-like shell replacement like Cygwin). The globbing functionality may or may not be performed by the C runtime's initialization depending on what tools and/or linker options you use:
if you're using Microsoft's compiler, the C runtime will not perform globbing by default, and you would get an argc value of 2 in your example. However, if you ask the linker to link in setargv.obj (or wsetargv.obj if you have a Unicode build), then globbing is added to the runtime initialization and you'll get behavior similar to Unix's. setargv.obj has been distributed with MSVC for as long as I can remember, but it's still little known. I believe that most Windows programs perform their own wildcard expansion.
if you're using the MinGW/GCC tool chain, the C runtime will perform globbing before calling main() (at least it does for MinGW 4.6.1 - I suspect it's been in MinGW for a long time). I think MinGW might not perform globbing for GUI programs. You can disable MinGW's globbing behavior with one of the following:
define a global variable named _CRT_glob and initialize it to 0:
int _CRT_glob = 0;
link in the lib/CRT_noglob.o object file (I think this might be order dependent - you may need to place it before any libraries):
gcc c:/mingw/lib/CRT_noglob.o main.o -o main.exe
The problem is that the shell expands * into all the file names (that don't start with a .) in the current directory. This is all about the shell and very little to do with the C program.
The value of argc includes 1 for the program's own name, plus one for each argument passed by the shell.
Try:
filename *
filename '*'
The first will give you 623 (give or take - but it is time you cleaned up that directory!). The second will give you 2.

Resources