Command line arguments

J

James Kuyper

Suppose a program expects two parameters, both of which contain wildcards.
The result will be a single list of files; how to tell where the first set
of files ends, and the next begins? Or the second parameter should be a
single file; how to tell whether that parameter was present? Etc.

Typically, that kind of situation is handled with options: command line
arguments that, by convention, start with a single '-' followed by one
or more single letter option identifiers, or by a "--" followed by an
option name. So, for example, you could write

myprogram -i *.in -o *.out

or

myprogram --input *.in --output *.out

This leads to potential problems: it's perfectly feasible to create a
file with a name that looks like a command line option. Some malware
creates files with such names, hoping to cause certain commands to
produce confusing or malignant consequences. However, in practice,
that's generally not a problem.
I've have a look later. If that works, that's good. But I can see problems:
it'll work on my system, but someone else running my program will also have
to do that set command. And it might stop other programs working properly
that expect the expansion.

It's not so much the C programs, as the users, that will have problems.
Instead of typing

wc *.c

the user will have to perform the expansion by hand, replacing *.c with
the complete list of file that *.c would have expanded to. That person
will NOT thank you for choosing noglob.
 
J

JohnF

BruceS said:
<<snip>> From a terminal (in Linux), type 'echo *', and it
echos a list of the files in that directory. Enclose in quotes (as
suggested elsethread), and type 'echo "*"', and get a single asterix.

No longer a C question, per se, but bash's behavior (at least on
my slackware 14.0 box) is a bit more complicated than suggested above.
echo * echoes a list of all files as stated
echo t*.c echoes test.c because that's the only such file in my /home
echo a*.c echoes a*.c apparently because there are no a*.c files at all
So if there are no matches, it reverts to echoing the string rather
than emitting an error like ls would. Is there a more general rule
behind that behavior? I ask because I also have a C program,
in this case evaluating command-line expressions which can be,
say, a*b, and I've never (so far) had any problem. Is that just
lucky because there never happened to be any filename matches?
 
S

Shao Miller

No longer a C question, per se, but bash's behavior (at least on
my slackware 14.0 box) is a bit more complicated than suggested above.
echo * echoes a list of all files as stated
echo t*.c echoes test.c because that's the only such file in my /home
echo a*.c echoes a*.c apparently because there are no a*.c files at all
So if there are no matches, it reverts to echoing the string rather
than emitting an error like ls would. Is there a more general rule
behind that behavior? I ask because I also have a C program,
in this case evaluating command-line expressions which can be,
say, a*b, and I've never (so far) had any problem. Is that just
lucky because there never happened to be any filename matches?

Well I'm not sure what else makes sense to do, in that situation. It
seems that any rule other than "pass what was provided, verbatim" would
be unnecessarily artificial. At least with the behaviour you describe,
a call to 'fopen' can fail and the program can report "File not found:
a*.c" (for a program expecting a list of filenames as arguments).
 
J

Johann Klammer

BartC said:
I've just discovered that a single command line argument containing
wildcards, such as *.c, is expanded to a full list of matching files before
it gets to main().

That isn't really what I want (and there could be thousands of matching
files, which I may not want to deal with in my C program, but pass on to
something else, or the argument may not be a file specification at all).

Is there any way this behaviour can be changed, without needing to write
arguments in a special way?

(On Windows, I can choose to use the WinMain entry point instead; but
this won't work under Linux, assuming that that also expands.)

I think there's a rant or two about this in the(somewhat dated)
'unix-haters handbook'. Very amusing.
 
G

glen herrmannsfeldt

No longer a C question, per se, but bash's behavior (at least on
my slackware 14.0 box) is a bit more complicated than suggested above.
echo * echoes a list of all files as stated
echo t*.c echoes test.c because that's the only such file in my /home
echo a*.c echoes a*.c apparently because there are no a*.c files at all
So if there are no matches, it reverts to echoing the string rather
than emitting an error like ls would.

Not quite.

ls gives the error when it tries to find the file a*.c and fails.
echo just prints its argument.

So bash does the same thing in both cases, with different results.

If a file names a*.c actually existed, then it would match a*.c,
and ls would list it.
Is there a more general rule
behind that behavior? I ask because I also have a C program,
in this case evaluating command-line expressions which can be,
say, a*b, and I've never (so far) had any problem. Is that just
lucky because there never happened to be any filename matches?

csh and tcsh will say

echo: No match.

(or whatever program you tried to run)

-- glen
 
8

88888 Dihedral

Shao Milleræ–¼ 2013å¹´1月11日星期五UTC+8上åˆ10時15分52秒寫é“:
And that's why, if you compile a POSIX- or UNIX-style program for

Windows, it'll usually include a startup routine to reproduce this

behaviour, since you can invoke your POSIX- or UNIX-style program from a

CLI/shell that _doesn't_ do this command-line expansion before passing

it on to the program.



--

"Thank you for the kind words; those are the kind of words I like to hear..



Cheerily," -- Richard Harter
Well, you are reminding me the dir function
from the M$ in the 1990 that refused to
do nested diretory search under 1M.
 
P

pu

Since a lot of the discussion is now about modifying the shell's default
behavior... be aware that *all* other programs expect the shell to do
globbing, so you'd have to modify them all to do the globbing themselves.

Not realistic.
 
J

Joe Pfeiffer

glen herrmannsfeldt said:
Not quite.

ls gives the error when it tries to find the file a*.c and fails.
echo just prints its argument.

So bash does the same thing in both cases, with different results.

If a file names a*.c actually existed, then it would match a*.c,
and ls would list it.

Trying this with ls, I was surprised to learn that JohnF's
interpretation is actually correct:

snowball:515$ ls a*.c
/bin/ls: cannot access a*.c: No such file or directory

So 'a*.c' must have actually been passed verbatim to /bin/ls.
 
S

Shao Miller

Trying this with ls, I was surprised to learn that JohnF's
interpretation is actually correct:

snowball:515$ ls a*.c
/bin/ls: cannot access a*.c: No such file or directory

So 'a*.c' must have actually been passed verbatim to /bin/ls.

It actually looks to me like everyone is in agreement about this bit.
Perhaps there was a misunderstanding.
 
G

glen herrmannsfeldt

pu said:
Since a lot of the discussion is now about modifying the shell's default
behavior... be aware that *all* other programs expect the shell to do
globbing, so you'd have to modify them all to do the globbing themselves.
Not realistic.

No, most users expect the shell to do the globbing.
Programs don't care at all.

If a specific users doesn't, then that is perfectly fine.

Also, it is safer as one doesn't have to worry about accidentally
typing

rm -rf *

and deleting too many files.

If you use subdirectories well, then all might be fine.

(Programs that do recursive operation, like rm and cp, do have
to be able to expand directory listings internally.)

I pretty often do something like

cd X*

if I know that there is only one directory (and no file) starting
with X.

-- glen
 
G

glen herrmannsfeldt

Joe Pfeiffer said:
Trying this with ls, I was surprised to learn that JohnF's
interpretation is actually correct:

snowball:515$ ls a*.c
/bin/ls: cannot access a*.c: No such file or directory
So 'a*.c' must have actually been passed verbatim to /bin/ls.

Yes.

I was surprised, too, as I usually use tcsh.

But it isn't that bash treats echo and ls differently.
(Which it could, as echo is an internal bash command, and
ls isn't.)

Shells like csh and tcsh will just give the error message
and not run the command (internal or external).

Bash passes the unexpanded form to the command, which may or
may not be what one wants, and may or may not generate a
message.

I suppose it is convenient with find, as one can say

cd /
find . -name *.c

as it would be rare to have any .c files in /, it would work.
Though I am pretty used to typing \*.c for find.

On the other hand,

vi *.c

would start editing *.c.

-- glen
 
G

glen herrmannsfeldt

(snip)
Well, you are reminding me the dir function
from the M$ in the 1990 that refused to
do nested diretory search under 1M.

The way command line expansion is done in current windows is
still surprising to unix users.

Unix ls, with shell globbing, will show the contents of the
directories in the expanded directory list.

dir *

on windows will show the contents of the current directory.

cd *

doesn't work right on Windows (even if there is only one directory)
but it does with unix shells.

And I still remember my first MSDOS system, version 3.2, where the
RENAME command wouldn't move files to a different directory
(like unix mv) but the C library rename() function would.

(The Windows MOVE command is much more recent.)

-- glen
 
S

Shao Miller

And I still remember my first MSDOS system, version 3.2, where the
RENAME command wouldn't move files to a different directory
(like unix mv) but the C library rename() function would.

(The Windows MOVE command is much more recent.)

While Standard C doesn't talk about "what's in a name" for the 'rename'
function, the DOS RENAME command does just what it says and nothing
more. :) I've always enjoyed this because if I'm far away, I don't have
to specify the long path twice:

ren i\m\way\up\here\file.ext newname.ext
 
J

Joe Pfeiffer

Shao Miller said:
It actually looks to me like everyone is in agreement about this
bit. Perhaps there was a misunderstanding.

Rereading Glen's post, I think I misunderstood him. My apologies.
 
G

glen herrmannsfeldt

(snip on unix and Windows command line globbing)
I don't think anyone is arguing that globbing is not helpful - in many
cases it is, but it's the wrong thing sometimes, and *nix has no good
way for a program to bypass it (and turning it off in the shell
doesn't count). You can't even access the raw command line to roll
your own if you wanted.

Seems to me that if you could it would be shell specific.

I wonder if the alias feature of any shell lets you get the raw
command line, quote it, then pass it to a program.

(Bash alias is somewhat different from csh alias.)
Of course some OS/library support for rolling your own would be nice
too at that point.

Other OS that process the command line in strange ways do, but then
you would be pretty stuck without it.

VMS does much processing on the command line before passing it
to usual commands, and so does CMS.

I suppose a shell could set an ENV variable for the program
to extract.

-- glen
 
S

Shao Miller

True, Windows requires you to enter at least one letter.

Continuing this bit of off-topic: I don't know what makes you say that.
In Windows XP, 'cd <wildcard_pattern>' will change directory into the
first matching directory. It so happens that the first matching
directory for any wildcard sequence that has no non-wildcard characters
is '.'; same as with 'dir *' (without additional switches). '..' also
matches, but not before '.'.
 
G

glen herrmannsfeldt

(snip, someone wrote)
Continuing this bit of off-topic: I don't know what makes you say that.
In Windows XP, 'cd <wildcard_pattern>' will change directory into the
first matching directory. It so happens that the first matching
directory for any wildcard sequence that has no non-wildcard characters
is '.'; same as with 'dir *' (without additional switches). '..' also
matches, but not before '.'.

This is a little complicated by the way windows CD works.

Among other things, you can CD to a directory name with spaces
without quoting the name, as you would for most other commands.

It does seem that

CD . ..

does nothing, and generates no error message.
(I don't have a directory named ". ..")

CD . .. Q

(when Q is the only subdirectory)

fails with an error message.

But yes, if more than one matches the pattern, it does
change to the first one with no message.

(Unlike unix shells, where cd complains if there is more than one.)

-- glen
 
G

Greg Martin

True, Windows requires you to enter at least one letter.




I don't think anyone is arguing that globbing is not helpful - in many
cases it is, but it's the wrong thing sometimes, and *nix has no good
way for a program to bypass it (and turning it off in the shell
doesn't count). You can't even access the raw command line to roll
your own if you wanted.

Of course some OS/library support for rolling your own would be nice
too at that point.


It seems to me that placing quotes around the argument *is* a good way
of handling it. Any program run from a command line and requiring
arguments will have to be specified and given that unix users are
inclined to read man pages and to ask a program itself for --help, it is
a simple thing to say "if you wish to use -f with wildcards, and don't
want the shell expansion, surround the argument with quotes". However,
_most_ unix users will know this already.
 
F

Fred K

glen herrmannsfeldt <[email protected]> writes: > JohnF <[email protected]> wrote: >> BruceS <[email protected]> wrote: >>> <<snip>> From a terminal (in Linux), type 'echo *', and it >>> echos a list of the files in that directory. Enclose in quotes (as >>> suggested elsethread), and type 'echo "*"', and get a single asterix. > >> No longer a C question, per se, but bash's behavior (at least on >> my slackware 14.0 box) isa bit more complicated than suggested above. >> echo * echoes a list of all files as stated >> echo t*.c echoes test.c because that's the only such file in my /home >> echo a*.c echoes a*.c apparently because there are no a*..c files at all >> So if there are no matches, it reverts to echoing the string rather >> than emitting an error like ls would. > > Not quite. > > ls gives the error when it tries to find the file a*.c and fails. > echo just prints its argument. > > So bash does the same thing in both cases, with different results. > > If a file names a*.c actually existed, then it would match a*.c, > and ls would list it. Trying this with ls, I was surprised to learn that JohnF's interpretation is actually correct: snowball:515$ ls a*.c /bin/ls: cannot access a*.c: No such file or directory So 'a*.c' must have actually been passed verbatim to /bin/ls. >> Is there a more general rule>> behind that behavior? I ask because I also have a C program, >> in thiscase evaluating command-line expressions which can be, >> say, a*b, and I've never (so far) had any problem. Is that just >> lucky because there never happened to be any filename matches? > > csh and tcsh will say > > echo: No match. > > (or whatever program you tried to run) > > -- glen


When there is no wild-card match, the Unix shell passes the actual as-typedstring or strings as arguments to the program.
Therefore " ls *.a *.b" will pass "*.a" and "*.b" as the arguments to ls
if there are no files with .a or .b extensions.

However, if there is at least one match, then the shell passes
the match(es) as argument(s).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,077
Messages
2,570,566
Members
47,202
Latest member
misc.

Latest Threads

Top