until/while и цепочка $?

В большинстве версий awk аргументы после исполняемой программы::

  1. Файл
  2. Присвоение видаx=y

Поскольку ваше имя файла интерпретируется как case #2, awk все еще ждет, пока что-то будет прочитано на stdin (, так как он не воспринимает, что было передано какое-либо имя файла ).

При переносе это поведение задокументировано в POSIX :

.

Either of the following two types of argument can be intermixed:

  • file: A pathname of a file that contains the input to be read, which is matched against the set of patterns in the program. If no file operands are specified, or if a file operand is '-', the standard input shall be used.
  • assignment: An operand that begins with an underscore or alphabetic character from the portable character set (see the table in the Base Definitions volume of IEEE Std 1003.1-2001, Section 6.1, Portable Character Set), followed by a sequence of underscores, digits, and alphabetics from the portable character set, followed by the '=' character, shall specify a variable assignment rather than a pathname.

Таким образом, при переносе у вас есть несколько вариантов (#1, вероятно, наименее навязчивый):

  1. Используйте awk..../my=file, который обходит это, поскольку .не является "символом подчеркивания или буквенным символом из переносимого набора символов".
  2. Поместите файл на стандартный ввод с помощью awk... < my=file. Однако это не работает с несколькими файлами.
  3. Временно создайте жесткую ссылку на файл и используйте ее. Вы можете сделать что-то вроде ln my=file my_file, а затем использовать my_fileкак обычно. Копирование выполняться не будет, и оба файла будут поддерживаться одними и теми же данными и метаданными inode. После его использования можно безопасно удалить созданную ссылку, так как количество ссылок на индексный дескриптор все равно будет больше 0.

2
13.03.2021, 19:34
2 ответа

Вы можете сделать что-то вроде:

false; while [[ $? -ne 0 ]]; do
  command
done

Здесь falseизначально устанавливает $?в ненулевое -значение, позволяя циклу запуститься по крайней мере один раз. Затем цикл выполняет данную команду до тех пор, пока не завершится с нулевым статусом выхода -.

1
18.03.2021, 22:25

Ключевые слова whileи untilпринимают в качестве аргумента команду . Если команда возвращает нулевой статус выхода -, сигнализирующий «успех», whileбудет запускать команды в теле цикла, а untilбудет , а не запускать команды в теле цикла. петля.

Похоже, что ваш код использует whileи untilс $?, статусом выхода -самой последней команды в качестве команды. Это не сработает, если в системе нет команд с целыми числами в качестве имен. Что будет работать с , так это использовать команду testдля сравнения с 0, например, с

until test "$?" -eq 0; do some-command; done

или, используя краткую форму test,

until [ "$?" -eq 0 ]; do some-command; done

Теперь очевидно, что проблема заключается в том, что $?вполне может быть нулевым при входе в цикл, предотвращая выполнение цикла даже один раз. Было бы более разумно позволить самой команде передать статус выхода -непосредственно либо while, либо until(, если только вы не хотите инициализировать $?ненулевым значением -, вызвав, например,. falseнепосредственно перед циклом, что выглядело бы странно ).

Использование стандартного POSIX (, хотя и не Bourne, не то, чтобы это имело большое значение в наши дни )синтаксис оболочки:

while ! some-command; do continue; done

или (как Bourne, так и POSIX ),

until some-command; do continue; done

Оба этих запуска выполняются some-commandдо тех пор, пока не вернут нулевой выход -статус ("успех" ). На каждой итерации он выполняет ключевое слово continueвнутри тела цикла. Выполнение continueнемедленно переходит к следующей итерации цикла,но вы можете заменить его, например. sleep 5, чтобы иметь небольшую задержку на каждой итерации, или с :, который ничего не делает.

Другим вариантом может быть бесконечный цикл, из которого вы выходите при успешном выполнении вашей команды:

while true; do some-command && break; done

или, немного более подробный и с добавлением воздуха для удобства чтения,

while true; do
    if some-command; then
        break
    fi
done

Единственный раз, когда вам действительно нужно для использования $?, это когда вам нужно какое-то время отслеживать это значение, как в

somefunction () {
    grep -q 'pattern' file
    ret=$?
    
    if [ "$ret" -ne 0 ]; then
        printf 'grep failed with exit code %s\n' "$ret"
    fi >&2
    
    return "$ret"
}

В приведенном выше коде статус выхода -в $?сбрасывается тестом [ "$ret" -ne 0 ], и мы не смогли бы напечатать его значение или вернуть его из функции, не сохранив его в отдельной переменной.


Еще одна вещь, на которую следует обратить внимание, это то, что вы, похоже, используете строку ,$command(без кавычек )в качестве команды. Было бы намного лучше использовать массив, если вы хотите запустить команду, хранящуюся в переменной:

thecommand=( awk -F ',' 'BEGIN { "uptime" | getline; split($4,a,": "); if (a[2] > 5) exit 1 }' )

while ! "${thecommand[@]}"; do sleep 5; done

(Код в этом примере приостанавливается с интервалом в пять секунд до тех пор, пока средняя загрузка системы за 1 -минут не опустится ниже 5.)

Обратите внимание, как с помощью массива и цитирования расширения как "${thecommand[@]}"я могу запустить свою команду, не беспокоясь о том, как оболочка разделит строку на слова и применит подстановку имени файла к каждому из этих слов.

См. также Как запустить команду, хранящуюся в переменной? для получения дополнительной информации по этому аспекту вашей проблемы.

15
18.03.2021, 22:25

Теги

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