Функция Bash с `getopts` работает только при первом запуске

В

head -n 1 file > file

file является усеченным прежде head запускается, но если Вы пишете это:

head -n 1 file 1<> file

это не как file открыт в режиме чтения-записи. Однако, когда head запись концов, это не усекает файл, таким образом, строка выше была бы не (head просто переписал бы первую строку по себе и оставил бы другие нетронутыми).

Однако после head возвратился и в то время как fd все еще открыто, можно назвать другую команду, которая делает truncate.

Например:

{ head -n 1 file; perl -e 'truncate STDOUT, tell STDOUT'; } 1<> file

Какие вопросы здесь это truncate выше, head просто перемещает курсор для fd 1 в файле сразу после первой строки. Это действительно переписывает первую строку, к которой мы не нуждались в нем, но это не вредно.

С головой POSIX мы могли на самом деле уйти, не переписывая ту первую строку:

{ head -n 1 > /dev/null
  perl -e 'truncate STDIN, tell STDIN'
} <> file

Здесь, мы используем факт это head перемещает позицию курсора в ее stdin. В то время как head обычно читал бы его вход большими блоками для улучшения производительности, POSIX потребует его (где возможный) к seek назад сразу после первой строки, если это пошло вне его. Обратите внимание однако, что не все реализации делают это.

С другой стороны, можно использовать оболочку read управляйте вместо этого в этом случае:

{ read -r dummy; perl -e 'truncate STDIN, tell STDIN'; } <> file
9
13.03.2019, 20:27
3 ответа

getopts bash используют переменную среды OPTIND для отслеживания последнего обработанного аргумента опции. Тот факт, что OPTIND не сбрасывался автоматически каждый раз, когда вы вызывали getopts в том же сеансе оболочки, а только при вызове оболочки. Итак, когда вы второй раз вызвали getopts с теми же аргументами в том же сеансе, OPTIND не был изменен, getopts думали, что они выполнили свою работу и ничего не сделали .

Вы можете сбросить OPTIND вручную, чтобы заставить его работать:

$ OPTIND=1
$ f -a 123
-a was triggered, Parameter: 123

или просто поместите функцию в скрипт и вызовите скрипт несколько раз.


zsh getopts немного отличается. OPTIND обычно сбрасывался в 1 каждый раз при выходе из функции оболочки.

15
27.01.2020, 20:04

Необходимо установить OPTIND = 1 в начале функции f . По умолчанию оно равно 1, но затем увеличивается по мере разбора аргументов. Когда вы вызываете getopts снова он несет на себе, где он ушел. Вы можете увидеть это, если ваш второй вызов:

f -a 123 -a 999

, когда он напечатает 999.

2
27.01.2020, 20:04

Когда вызывается getopt , он отслеживает обработанные параметры с помощью переменной OPTIND .

Попробуйте следующее:

#!/bin/bash

f () {
    printf "Intro OPTIND: %d\n" "$OPTIND"
    while getopts ":a:b:" opt; do
        printf "Current OPTIND: %d\n" "$OPTIND"
        case $opt in
            a)
                echo "-a was triggered, Parameter: $OPTARG" >&2
                ;;
            b)
                echo "-b was triggered, Parameter: $OPTARG" >&2
                ;;
        esac
    done
    printf "Exit OPTIND: %d\n" "$OPTIND"
}

echo "Run #1"
f "$@"
echo "Run #2"
f "$@"

Выход:

./test -a foo -b bar
Run #1
Intro OPTIND: 1
Current OPTIND: 3
-a was triggered, Parameter: foo
Current OPTIND: 5
-b was triggered, Parameter: bar
Exit OPTIND: 5
Run #2
Intro OPTIND: 5
Exit OPTIND: 5

Таким образом, вы можете сделать что-то вроде:

OPTIND=1

в начале функции. Или, в зависимости от ситуации, что обычно лучше:

local OPTIND

Если OPTIND не использовался, поскольку функция реализована, цикл while продолжался бы вечно. Его также можно использовать для возобновления обработки аргументов, после сбоя или чего-либо еще, вызвать другую функцию, если x или y, и она возобновит работу с того места, где было остановлено ранее и т. Д.

1
27.01.2020, 20:04

Теги

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