There's a longer debate about this topic on Stackoverflow titled "Argument passing strategy - environment variables vs. command line". [1]
[1]: http://stackoverflow.com/questions/7443366/argument-passing-...
— It is possible to “see” a command line (in "ps" output, etc.) in ways that you won’t see environment settings. This can be useful when passing information to a sub-process that you need to keep private.
— It may be that you are configuring your sub-process in a place that is “far away” from the point that actually executes the command. Rather than have to thread an extra command-line argument through your code to make sure it is part of the final command invocation, it can be quite convenient to just set a variable. I have often used this to enable debugging features or test experimental features, or even to disable entire features when unexpected problems arise.
— In a similar way, your program may use multiple languages or otherwise be difficult to manage in any common way without environment variables.
— In a cross-platform scenario, environment variable names might be far easier to keep constant across UNIX, Windows, etc. than command-line syntax.
I’m sure there are other reasons.
One argument for having command-line arguments is that it can be less typing, as argument names can be implied. Compare:
>cp foo bar
with >from=foo to=bar cp
It also seems easier to me to specify multiple arguments from the command line, but that probably could be solved (mostly) by changing sh syntax.Also, I've never understood where this exit(-1) idiom comes from. It's nonsense.
Within that explanation, we all know that global/shared variables are a code-smell for the most part. Say you want to call the same command with different logic, or multiple times even.
result1 = func("hello", "world"); result2 = func("hello", "another world");
vs
greeting = "hello"; greetee = "world";
result1 = func(); //How do we even know that func uses greeting and greetee variables?!
greetee = "another world"; result2= func(); //Did func change my greeting variable? I don't know.
So let's assume that greeting and greetee are actual important variables. You are essentially then sharing your "state" with the func in order to alter its behavior. I think in some shells, the functions themselves can alter global environment variables, so it would be a giant mess making sure that functions are idempotent and don't have artifacts.
The difference occurs when a process A created process B and process B spawns one or more processes.
Environment variables also made shell scripts reusable by other users. The file $HOME/special would refer to the "special" file in the user's home directory.
Command line arguments are only passed to the one single child process. And if that process wants to launch a new process, it must create it's own command line arguments.
Also dynamic scoping can be very powerful when stitching together pieces separately designed. To this day emacs lisp is still dynamically scoped by default and arguably it derives some of its power from it.
I still hate environment variables.
echo *.txt | read_stdin_and_get_filenames
You can use environment variables: export SPOOLDIR=$HOME/myspooldir;
empty_spool_directory
You can use a single argument: echo *.txt >list_of_filenames
mycomand list_of_filenames~~Also, the paired asterisks ate some of your comment by treating it as an italics marker.~~ fixed now, thanks.
(that should read star dot txt in the line above, not sure how to disable the italics meaning of star in posts)
I don't think it is the case in Windows, and this seems not to have changed since DOS days, when some programs would be abled to handle wildcards (internally) while others could not, because it was done by the individual programs, not the shell (COMMAND.COM or nowadays CMD.EXE).
A quick test:
$ python -V Python 3.5.2
$ type test_arg_list.py
import sys print(sys.argv)
$ python test_arg_list.py a t b ['test_arg_list.py', 'a', 't', 'b']
(that should read t star (not just t) in both the lines above)
So wildcards are not expanded. I'm sure there are Windows calls to expand them (there were from the DOS days, like FindFirst and FindNext (awkward approach, IMO), but your program has to actually use them for the expansion to work.
In fact, that is what I did, via the Python glob module, in this recent post:
Simple directory lister with multiple wildcard arguments:
https://jugad2.blogspot.in/2016/12/simple-directory-lister-w...
Whereas, in Unix, the shell (at least sh / bash) does it automatically for all arguments for all command-line programs, before the program even sees the arguments. This is one of the (many) key benefits of the shell. In fact, all metacharacters are interpreted by the shell and/or the kernel, acting together. This includes redirections, piping, the many special symbols that start with $, backquotes, and many others.
I think I may have the answer (at least for Unix). (IIRC had read about this somewhere a while ago, also had thought about this issue myself earlier, so it's a combo of reading the reason somewhere and (maybe) figuring it out. Anyway, here it is:
It is because it allows 3 different ways of setting options for commands: rc files, environment variables (env. vars from now) and command line arguments, with each subsequent one able to override the previous one. The logic being that they go, in order, from more permanent to less permanent (as settings). rc files (rc stands for run command, a term I think I read Unix inherited from some previous OS) are config files for commands, like .exrc and .vimrc for vi/vim, .bashrc for bash, .netrc and many more. Any command can create or require users to create its own rc file, and can use it if present to read settings. The setting in a file is less easy to change on the fly than an env. var (not really difficult, of course, just that you have to go edit that file in an editor - or use sed etc.), and an env. var in turn is (a bit) less easy to change than a command line option, when we are talking about multiple different invocations of a command, in which you want the values for that option to be different in some of the invocations.
Let's take the example of a setting for a port (for a network server or client):
First, put the most common and permanent setting for the option, say, PORT=8080, in the rc file, say .foorc (for command foo - whether foo is built-in or written by you).
Second, for times when you want to change it for say today's work, set (i.e. change) it via an env. var, like:
export PORT=8181
foo args ...
# this setting will remain in effect until you change the var or you logout/reboot, and as long as it is present, will override any PORT value in .foorc each time you run foo.
It can also be shortened to:
PORT=8181 foo args ...
# but this is now a one-time setting of the env. var, so will override any PORT value in .foorc for this run only.
# In both the above variants, the args will not include PORT, since the foo command will be written to check for an env. var called PORT internally (and similarly checks for a PORT setting in .foorc before checking for an env. var called PORT, with the latter overriding the former if both are present).
And third, for the time(s) when you want to change the PORT setting on the fly, maybe just once for today, do:
foo --port 8282 args
which will override the settings for port (if any) in both the rc file and the env. var.
So the order is: command line option overrides env. var. and env. var overrides rc file setting.
This is what I read/figured out. It gives a lot of flexibility. Many Unix commands work that way. If you want your own to work that way, you have to write the code for it, like checking for presence of the rc file and for the setting in it, checking for the env. var with getenv() and finally checking for the command line option.
Edit: for typos.