POSIXly, the parsing for options should stop at --
or at the first non-option (or non-option-argument) argument whichever comes first. So in
cp -R file1 -t /mybackup file2 -f
that's at file1
, so cp
should recursively copy all of file1
, -t
, /mybackup
and file2
into the -f
directory.
GNU getopt(3)
however (that GNU cp
uses to parse options (and here you're using GNU cp
since you're using the GNU-specific -t
option)), unless the $POSIXLY_CORRECT
environment variable is set, accepts options after arguments. So it is actually equivalent to POSIX option style parsing's:
cp -R -t /mybackup -f -- file1 file2
The getopts
shell built-in, even in the GNU shell (bash
) only handles the POSIX style. It also doesn't support long options or options with optional arguments.
If you want to parse the options the same way as GNU cp
does, you'll need to use the GNU getopt(3)
API. For that, if on Linux, you can use the enhanced getopt
utility from util-linux
(that enhanced version of the getopt
command has also been ported to some other Unices like FreeBSD).
That getopt
will rearrange the options in a canonical way which allows you to parse it simply with a while/case
loop.
$ getopt -n "$0" -o t:Rf -- -Rf file1 -t/mybackup file2
-R -f -t '/mybackup' -- 'file1' 'file2'
You'd typically use it as:
parsed_options=$(
getopt -n "$0" -o t:Rf -- "$@"
) || exit
eval "set -- $parsed_options"
while [ "$#" -gt 0 ]; do
case $1 in
(-[Rf]) shift;;
(-t) shift 2;;
(--) shift; break;;
(*) exit 1 # should never be reached.
esac
done
echo "Now, the arguments are $*"
Also note that that getopt
will parse options the same way as GNU cp
does. In particular, it supports the long options (and entering them abbreviated) and honours the $POSIXLY_CORRECT
environment variables (which when set disables support for options after arguments) the same way GNU cp
does.
Note that using gdb and printing the arguments that getopt_long()
receives can help building the parameters to getopt(1)
:
(gdb) bt
#0 getopt_long (argc=2, argv=0x7fffffffdae8, options=0x4171a6 "abdfHilLnprst:uvxPRS:T", long_options=0x417c20, opt_index=0x0) at getopt1.c:64
(gdb) set print pretty on
(gdb) p *long_options@40
$10 = {{
name = 0x4171fb "archive",
has_arg = 0,
flag = 0x0,
val = 97
}, {
name = 0x417203 "attributes-only",
[...]
Then you can use getopt
as:
getopt -n cp -o abdfHilLnprst:uvxPRS:T -l archive... -- "$@"
Remember that GNU cp
's list of supported options may change from one version to the next and that getopt
will not be able to check if you pass a legal value to the --sparse
option for instance.
export VARIABLE_NAME='some value'
is the way to set an environment variable in any POSIX-compliant shell (sh
, dash
, bash
, ksh
, etc.; also zsh). If the variable already has a value, you can use export VARIABLE_NAME
to make it an environment variable without changing its value.
Pre-POSIX Bourne shells did not support this, which is why you'll see scripts that avoid export VARIABLE_NAME='some value'
and use VARIABLE_NAME='some value'; export VARIABLE_NAME
instead. But pre-POSIX Bourne shells are extremely rare nowadays.
setenv VARIABLE_NAME='some value'
is the csh syntax to set an environment variable. setenv
does not exist in sh, and csh is extremely rarely used in scripts and has been surpassed by bash for interactive use for the last 20 years (and zsh for even longer), so you can forget about it unless you encounter it.
The env
command is very rarely useful except in shebang lines. When invoked without arguments, it displays the environment, but export
does it better (sorted, and often quoted to disambiguate newlines in values from newlines that separate values). When invoked with arguments, it runs a command with extra environment variables, but the same command without env
also works (VAR=value mycommand
runs mycommand
with VAR
set to value
, just like env VAR=value mycommand
). The reason env
is useful in shebang line is that it performs PATH
lookup, and it happens to not do anything else when invoked with a command name. The env
command can be useful to run a command with only a few environment variables with -i
, or without parameters to display the environment including variables with invalid names that the shell doesn't import.
Best Answer
If it has to be portable to a range of Unices, you'd have to stick to POSIX sh. And AFAIU there you just have no choice but rolling argument handling by hand.