Да, как требуется POSIX, команды, запущенные в фоне с &
имейте их стандартный вход, перенаправленный от /dev/null
.
И действительно
{ cmd <&3 3<&- & } 3<&0
самый очевидный путь состоит в том, чтобы работать вокруг этого.
Не ясно, почему Вы хотели бы выполнить часть конвейера в фоне все же.
В соответствии с POSIX синтаксический анализ параметров должен останавливаться на -
или на первом аргументе без параметра (или без аргумента-параметра), в зависимости от того, что наступит раньше. Итак, в
cp -R file1 -t /mybackup file2 -f
это в file1
, поэтому cp
должен рекурсивно копировать все file1
, -t
, / mybackup
и file2
в каталог -f
.
GNU getopt (3)
однако (который GNU cp
использует для анализа параметров (и здесь вы используете GNU cp
, поскольку вы используете GNU-specific -t
option)), если не установлена переменная среды $ POSIXLY_CORRECT
, принимает параметры после аргументов. Таким образом, это фактически эквивалентно синтаксическому анализу стиля опций POSIX:
cp -R -t /mybackup -f -- file1 file2
Встроенная оболочка getopts
, даже в оболочке GNU ( bash
), обрабатывает только стиль POSIX. Он также не поддерживает длинные параметры или параметры с необязательными аргументами.
Если вы хотите анализировать параметры так же, как это делает GNU cp
, вам нужно использовать API GNU getopt (3)
. Для этого, если в Linux, вы можете использовать расширенную утилиту getopt
из util-linux
(, эта расширенная версия команды getopt
также был перенесен на некоторые другие системы Unix, такие как FreeBSD ).
Этот getopt
изменит порядок параметров каноническим способом, что позволит вам просто проанализировать его с помощью цикла while / case
.
$ getopt -n "$0" -o t:Rf -- -Rf file1 -t/mybackup file2
-R -f -t '/mybackup' -- 'file1' 'file2'
Обычно вы используете его как:
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 $*"
Также обратите внимание, что getopt
анализирует параметры так же, как GNU cp
. В частности, он поддерживает длинные параметры (и ввод их сокращенно) и учитывает переменные среды $ POSIXLY_CORRECT
(которые при установке отключают поддержку параметров после аргументов) точно так же, как это делает GNU cp
.
Обратите внимание, что использование gdb и печать аргументов, которые получает getopt_long ()
, может помочь в построении параметров для 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",
[...]
Затем вы можете использовать getopt
как:
getopt -n cp -o abdfHilLnprst:uvxPRS:T -l archive... -- "$@"
Помните, что список поддерживаемых опций GNU cp
может изменяться от одной версии к другой и что getopt
не сможет проверить, пройдете ли вы допустимое значение для параметра - разреженный
, например.
Таким образом, каждый раз, когда getopts
обрабатывает аргумент, он не ожидает, что он устанавливает переменную оболочки $ OPTIND
на следующий номер в списке аргументов, который он должен обработать и вернуть, кроме 0. Если $ OPTIND
установлен в значение 1, getopts
указывается в POSIX для принятия нового списка аргументов. Таким образом, это просто наблюдает за возвращением getopts
, сохраняет приращения счетчика плюс $ OPTIND
для любого неудачного возврата, сдвигает неудачные аргументы и сбрасывает $ OPTIND
каждые неудачная попытка. Вы можете использовать его как opts "$ @"
- хотя вам нужно настроить цикл case
или сохранить его в переменной и изменить этот раздел на eval $ дело
.
opts() while getopts :Rt:f opt || {
until command shift "$OPTIND" || return 0
args=$args' "${'"$((a=${a:-0}+$OPTIND))"'}"'
[ -n "${1#"${1#-}"}" ]
do OPTIND=1; done 2>/dev/null; continue
}; do case "$opt" in
R) Rflag=1 ;;
t) tflag=1 ;
targ=$OPTARG ;;
f) fflag=1 ;;
esac; done
При запуске он устанавливает $ args
для каждого аргумента, который getopts
не обрабатывает ... так что ...
set -- -R file1 -t /mybackup file2 -f
args= opts "$@"; eval "set -- $args"
printf %s\\n "$args"
printf %s\\n "$@"
printf %s\\n "$Rflag" "$tflag" "$fflag" "$targ"
"${2}" "${5}"
file1
file2
1
1
1
/mybackup
Это работает в bash
, тире
, zsh
, ksh93
, mksh
... ну, я прекратил попытки на этом этапе. В каждой оболочке также есть флаг $ [Rtf]
и $ targ
. Дело в том, что все числа для аргументов, которые getopts
не хотели обрабатывать, остались.
Изменение стиля опций тоже не имело значения. Он работал как -Rf -t / mybackup
или -R -f -t / mybackup
. Это работало в середине списка, в конце списка или в начале списка ...
Тем не менее, самый лучший способ - это просто вставить - -
для завершения параметров в вашем списке аргументов, а затем выполните shift "$ (($ OPTIND-1))"
в конце выполнения обработки getopts
. Таким образом вы удалите все обработанные параметры и сохраните конец списка аргументов.
Мне нравится переводить длинные варианты в короткие - и я делаю это очень похожим образом, поэтому этот ответ пришел легко - до Я запускаю getopts
.
i=0
until [ "$((i=$i+1))" -gt "$#" ]
do case "$1" in
--Recursive) set -- "$@" "-R" ;;
--file) set -- "$@" "-f" ;;
--target) set -- "$@" "-t" ;;
*) set -- "$@" "$1" ;;
esac; shift; done