Подтвердить что ты не робот

Bash замена и синхронизация процессов

(Возможно, связано с Некоторые программы не принимают замену процессов для входных файлов?)

В некоторых сценариях Bash unit test я использую следующий трюк для регистрации и отображения stdout и stderr команды:

command > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2)

Этот процесс производит некоторый вывод в stdout, поэтому файл $stdoutF получает некоторые данные. Затем я запускаю еще одну команду, которая не выводит никаких данных:

diff -r "$source" "$target" > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2)

Однако это не похоже, что этот процесс всегда успешно завершается до запуска теста на пустоту (используя shunit-ng):

assertNull 'Unexpected output to stdout' "$(<"$stdoutF")"

В тесте 100 run это не удалось 25 раз.

Достаточно вызвать sync перед тестированием файла для пустоты:

sync
assertNull 'Unexpected output to stdout' "$(<"$stdoutF")"

... и/или он должен работать, заставляя последовательность команд:

diff -r "$source" "$target" \
> >(tee "${stdoutF}"; assertNull 'Unexpected output to stdout' "$(<"$stdoutF")")
2> >(tee "${stderrF}" >&2)

... и/или возможно ли tee как-то assertNull непосредственно вместо файла?

Обновление: sync не является ответом - см. ответ Жиля ниже.

Обновление 2. Далее обсуждается синхронизировать stdout, stderr и stdout + stderr. Спасибо за ответы!

4b9b3361

Ответ 1

В bash команда замены подстановки foo > >(bar) завершается, как только заканчивается foo. (Это не обсуждается в документации.) Вы можете проверить это с помощью

: > >(sleep 1; echo a)

Эта команда немедленно возвращается, а затем выводит a асинхронно через секунду.

В вашем случае команда tee занимает только один бит времени для завершения после завершения command. Добавление sync дало tee достаточно времени для завершения, но это не устраняет условие гонки, не более, чем добавление sleep, это просто делает гонку более маловероятной для проявления.

В общем случае sync не имеет видимого внутри себя эффекта: имеет значение только если вы хотите получить доступ к устройству, где ваши файловые системы хранятся в другом экземпляре операционной системы. В более четких выражениях, если ваша система теряет мощность, после перезагрузки будут доступны только данные, записанные до последнего sync.

Что касается снятия условия гонки, вот несколько возможных подходов:

  • Явно синхронизировать все замещенные процессы.

    mkfifo sync.pipe
    command > >(tee -- "$stdoutF"; echo >sync.pipe)
           2> >(tee -- "$stderrF"; echo >sync.pipe)
    read line < sync.pipe; read line < sync.pipe
    
  • Используйте другое временное имя файла для каждой команды вместо повторного использования $stdoutF и $stderrF и убедитесь, что временный файл всегда создан.

  • Откажитесь от замены процесса и используйте трубы вместо этого.

    { { command | tee -- "$stdoutF" 1>&3; } 2>&1 \
                | tee -- "$stderrF" 1>&2; } 3>&1
    

    Если вам нужен статус возврата команды, bash помещает его в ${PIPESTATUS[0]}.

    { { command | tee -- "$stdoutF" 1>&3; exit ${PIPESTATUS[0]}; } 2>&1 \
                | tee -- "$stderrF" 1>&2; } 3>&1
    if [ ${PIPESTATUS[0]} -ne 0 ]; then echo command failed; fi
    

Ответ 2

Я иногда ставил охранника:

: > >(sleep 1; echo a; touch guard) \
  && while true; do
    [ -f "guard" ] && { rm guard; break; }
     sleep 0.2
  done    

Ответ 3

Вставьте sleep 5 или еще что-то вместо sync, чтобы ответить на ваш последний вопрос