问题
Background:
I'm writing a bash script that must receive these arguments:
- a file name (a file containing a set of rules )
- a list of file names ( files to be processed, can use wildcards)
- a destination folder ( where processed versions of files will be stored )
In theory there are 3 parameters but in reality the second argument expands, so the real number of argument varies if the wildcard matches more than one file:
When I call
./myscript file.conf *.data dest_foder
*.data
expands into as many files match the wildcard so it's equivalent to:
myscript file.conf this.data that.data so.data dest_foder
Prior to processing I need to validate that the last argument is a folder and not a file.
The problem
If I ommit the destination folder, like:
myscript file.conf *.data
The validation ?# -ge 3
passes because the wildcard returns more than one item, and if a folder that matches the wildcard happens to exist, like this_is_a_folder.data
, then the expansion is like this:
myscript file.conf this.data that.data so.data this_is_a_folder.data
The script will work but in a non-expected way... it will process this.data, that.data and so.data
and put the processed versions of the files in the folder this_is_a_folder.data
.
Question
- How do I avoid that ?
- How can I validate that the destination folder was explicitly passed as an argument and not the product of the expansion of the wildcard in the second parameter ?
I cannot find a way to read the literal "*.data" argument before it's expanded into matching filenames.
Clarifying:
I know how to read the last argument.
- But how do I know whether that last argument was explicitly passed or was the product a wild card expansion ?
回答1:
You can't do this, since the shell expands any wildcards in the command line before your script even starts. When you enter ./myscript file.conf *.data dest_foder
in the shell, this is effectively just a shorthand for ./myscript file.conf this.data that.data so.data dest_foder
, not a different command.
If you need the wildcard passed into the command as an actual argument, you need to quote or escape it. Something like ./myscript file.conf '*.data' dest_foder
or ./myscript file.conf \*.data dest_foder
. Alternately, make the last argument mandatory, or turn it into an option (-d dest_folder
) so the preexpanded file list isn't a problem.
Put it another way: it'd be very convenient if you didn't have to quote/escape wildcards in your grep patterns; but you have to, because there's no way for the grep
command to get at its arguments in unexpanded form. And if the authors of grep
couldn't figure out how to make their command more convenient, there's no way you're going to be able to do it...
回答2:
Your script can not know, since it is passed the arguments by the shell, and it will have no notion of the state of the calling process. Why not
- Reorder your arguments so that the variable length arguments come last
- Use commandline flags such as --dest/-d DEST_DIR
This would be more consistent with UNIX tool conventions.
来源:https://stackoverflow.com/questions/18060389/how-to-access-literal-wildcard-argument-before-its-expanded-to-matching-files