I was just looking at executing a program with command line arguments. I see that argument count (argc) is implicit and that the computer calculates that by looking at the number of arguments I give.
I just wondered out of curiousity whether or not it is possible or me to explicitly declare the value of argc?
No, argc is passed into the main() method. It is supplied at runtime.
Related
Everytime I create a project (standard command line utility) with Xcode, my
main function starts out looking like this:
int main(int argc, const char * argv[])
What's all this in the parenthesis? Why use this rather than just
int main()?
main receives the number of arguments and the arguments passed to it when you start the program, so you can access it.
argc contains the number of arguments, argv contains pointers to the arguments.
argv[argc] is always a NULL pointer. The arguments usually include the program name itself.
Typically if you run your program like ./myprogram
argc is 1;
argv[0] is the string "./myprogram"
argv[1] is a NULL pointer
If you run your program like ./myprogram /tmp/somefile
argc is 2;
argv[0] is the string "./myprogram"
argv[1] is the string "/tmp/somefile"
argv[2] is a NULL pointer
Although not covered by standards, on Windows and most flavours of Unix and Linux, main can have up to three arguments:
int main(int argc, char *argv[], char *envp[])
The last one is similar to argv (which is an array of strings, as described in other answers, specifying arguments to the program passed on the command line.)
But it contains the environment variables, e.g. PATH or anything else you set in your OS shell. It is null terminated so there is no need to provide a count argument.
These are for using the arguments from the command line -
argc contains the number of arguments on
the command line (including the program name), and argv is the list of
actual arguments (represented as character strings).
These are used to pass command line paramters.
For ex: if you want to pass a file name to your process from outside then
myExe.exe "filename.txt"
the command line "filename.txt" will be stored in argv[], and the number of command line parameter ( the count) will be stored in argc.
main() is a function which actually can take maximum three parameters or no parameters.
The following are the parameters that main() can take is as follows:-
1) int argc : It holds the number of arguments passed (from the command prompt) during the execution of program or you can say it is used ot keep a track of the number of variables passed during the execution of program. It cannot hold the negative value. Eg. If you pass your executable file "./a.out" then that will be considered as a parameter and hence argc value will be 0 i.e 1 value is passed.
2) char *argv[] : it is an array of character pointers which holds the address of the strings(array of characters) that are passed from the command prompt during execution of program. Eg. in above example if you wrote argv[argc] i.e argv[0] in cout then it will give ./a.out as output.
3) char*envp[] : it is also an array of character pointer which is used to hold the address of the environments variables being used in the program. Environment variables are the set of dynamic named value that can affect the way the running process will behave on the computer. For example running process want to store temporary files then it will invoke TEMP environment variables to get a suitable location.
Just like getting the function calls with ${FUNCNAME[#]}, is there a way to get the commands? BASH_COMMAND can only be used to get the last command (it's not an array, just a string).
I know I can achieve that by using BASH_SOURCE and BASH_LINENO to read the right line from the right file, but it does not work in case of evals (see my other, less-specific question Get the contents of an expanded expression given to eval through Bash internals)
Is there another way?
What is your intent? If you want to print a stack trace, you can use the Bash builtin command caller, like this:
dump_trace() {
local frame=0 line func source n=0
while caller "$frame"; do
((frame++))
done | while read line func source; do
((n++ == 0)) && {
printf 'Stack trace:\n'
}
printf '%4s at %s\n' " " "$func ($source:$line)"
done
}
From Bash manual:
caller [expr]
Returns the context of any active subroutine call (a shell function or
a script executed with the . or source builtins).
Without expr, caller displays the line number and source filename of
the current subroutine call. If a non-negative integer is supplied as
expr, caller displays the line number, subroutine name, and source
file corresponding to that position in the current execution call
stack. This extra information may be used, for example, to print a
stack trace. The current frame is frame 0.
The return value is 0 unless the shell is not executing a subroutine
call or expr does not correspond to a valid position in the call
stack.
See the full logging/error handling implementation here:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
Simple answer: there's no way to do that in Bash.
Related to the linked question and eval: Zsh seems to handle evals better, with variables and arrays such as EVAL_LINENO, zsh_eval_context and others.
funcstack
This array contains the names of the functions, sourced files, and (if EVAL_LINENO is set) eval commands. currently being executed. The first element is the name of the function using the parameter.
The standard shell array zsh_eval_context can be used to determine the type of shell construct being executed at each depth: note, however, that is in the opposite order, with the most recent item last, and it is more detailed, for example including an entry for toplevel, the main shell code being executed either interactively or from a script, which is not present in $funcstack.
See man zshall for more details.
I need to process an argument to a bash script that might or might not have a leading pound sign (octothorpe). The simplest example is:
echo #1234
which returns nothing
It might be because it processes the text as a command and assumes it is a comment.
$#, $*, etc. do not work. getopts does not seem to address this sort of thing.
Suggestions welcome
This is completely impossible, because the "argument" in question is parsed as a comment and never passed to the command at all.
Keep in mind that programs in C have the following calling convention for their main function:
int main(int argc, char *argv[])
This means that programs are passed a list of individual, separate arguments, not a single string that isn't yet parsed. The original string from which that vector of arguments was parsed is not given to the invoked program at all; often, no "original string" even exists. Consequently, a program that was invoked has no way to "unring the bell" and go back from the parsed list of strings to the original string from which it was generated.
Consequently, if your script is invoked as an external command (as opposed to a shell function), the invocation of the shell that runs it by the operating system will go through the execve syscall, which takes as its arguments (1) the file to execute; (2) the argument vector to pass it (which is to say, the aforementioned list of individual C strings); and (3) a list of environment variables. There is no argument for an unparsed shell command line, so no such content is available to the subprocess.
Train your users to use appropriate quoting. All of the below will have completely indistinguishable behavior, insofar as yourscript is concerned:
yourscript '#1234' # single quotes prevent content from being parsed as shell syntax
yourscript ''#1234 # "#" only begins a comment at the front of a string
yourscript '#'1234 # note that shell quoting is character-by-character
yourscript \#1234 # ...so either quoting or escaping only that single character suffices.
...any of the above will pass an argv containing (in C syntax) char[][]{ "yourscript", "#1234", NULL }
Say I have a bash script with two optional arguments
How would I be able to run the script providing an input for the second argument, but not the first argument?
The shell's argument list is just a sequence of strings. There is no way for the first string to be undefined and the second to be defined, but if you have control over the program, or the person who wrote it anticipated this scenario, perhaps it supports passing in an empty first argument, or perhaps a specific string which is interpreted as "undefined".
To pass in an empty string, the shell allows you to put two adjacent quotes (which will be removed by the shell before the argument is passed on to the program you are running, by way of how quotes are handled by the shell in general).
program '' second third fourth
A common related convention is to let a lone or double dash signify "an option which isn't an option".
program -- second third fourth
If you have control over the command and its argument handling (and it's not already cemented because you have programs written by other people which depend on the current behavior) a better design would be to make the optional argument truly optional, i.e. maybe make the first argument a dash option.
program --uppercase string of arguments
program --lowercase STRING OF SHOUTING
program The arguments will be passed through WITHOUT case conversion
The implementation is straightforward:
toupper () { tr '[:lower:]' '[:upper:]'; }
tolower () { tr '[:upper:]' '[:lower:]'; }
case $1 in
--uppercase) shift; toupper;;
--lowercase) shift; tolower;;
*) cat;;
esac <<<"$#"
If the behavior is cemented, a way forward is to create a command with a different name with the same core behavior but with better command-line semantics, and eventually phase out the old version with the clumsy argument handling.
I have a variable in my shell script that needs to declared as follows:
MY_VAR="/path/to/exec -options < inputfile"
This is the standard way the executable takes the input. Now, if I do $MY_VAR, the program quits with an error too many arguments. I suspect it is the < sign that is causing the problem. Any way I can get a workaround without splitting the statement into two variables?
Thanks
You could use eval, but that is considered harmful (see BashFAQ). Try to find a better solution that does not need the whole command line in a variable. For example, use a function:
my_func() {
/path/to/exec -options < inputFile
}