колотите, как удалить опции из параметров после обработки

Да, как требуется POSIX, команды, запущенные в фоне с & имейте их стандартный вход, перенаправленный от /dev/null.

И действительно

{ cmd <&3 3<&- & } 3<&0

самый очевидный путь состоит в том, чтобы работать вокруг этого.

Не ясно, почему Вы хотели бы выполнить часть конвейера в фоне все же.

9
23.05.2017, 14:33
2 ответа

В соответствии с 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 не сможет проверить, пройдете ли вы допустимое значение для параметра - разреженный , например.

9
27.01.2020, 20:07

Таким образом, каждый раз, когда 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"

OUTPUT

 "${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
1
27.01.2020, 20:07

Теги

Похожие вопросы