Не существует надежного способа определить, передаются ли STDIN, STDOUT или STDERR в / из вашего сценария, в основном из-за таких программ, как ssh
.
Вещи, которые "нормально" работают
Например, следующее решение bash правильно работает в интерактивной оболочке:
[[ -t 1 ]] && \
echo 'STDOUT is attached to TTY'
[[ -p /dev/stdout ]] && \
echo 'STDOUT is attached to a pipe'
[[ ! -t 1 && ! -p /dev/stdout ]] && \
echo 'STDOUT is attached to a redirection'
Но они не всегда работают
Однако при выполнении этой команды как команды, отличной от TTY ssh
, потоки STD всегда выглядят так, как будто они передаются по конвейеру. Чтобы продемонстрировать это, используйте STDIN, потому что это проще:
# CORRECT: Forced-tty mode correctly reports '1', which represents
# no pipe.
ssh -t localhost '[[ -p /dev/stdin ]]; echo ${?}'
# CORRECT: Issuing a piped command in forced-tty mode correctly
# reports '0', which represents a pipe.
ssh -t localhost 'echo hi | [[ -p /dev/stdin ]]; echo ${?}'
# INCORRECT: Non-tty mode reports '0', which represents a pipe,
# even though one isn't specified here.
ssh -T localhost '[[ -p /dev/stdin ]]; echo ${?}'
Почему это важно
Это довольно большое дело, потому что это означает, что сценарий bash не может определить, передается ли не-tty ssh
команда или нет. Обратите внимание, что это неудачное поведение было введено, когда последние версии ssh
начали использовать каналы для не-TTY STDIO. В предыдущих версиях использовались сокеты, которые МОГУТ различать изнутри bash с помощью [[ -S ]]
.
Когда это важно
Это ограничение обычно вызывает проблемы, когда вы хотите написать bash-скрипт, который ведет себя как скомпилированная утилита, например cat
. Например, cat
допускает следующее гибкое поведение при одновременной обработке различных источников ввода и достаточно умен, чтобы определить, получает ли он ввод по каналу независимо от того, используется ли не TTY или принудительный TTY ssh
:
ssh -t localhost 'echo piped | cat - <( echo substituted )'
ssh -T localhost 'echo piped | cat - <( echo substituted )'
Вы можете сделать что-то подобное, только если сможете достоверно определить, задействованы ли трубы или нет. В противном случае выполнение команды, которая читает STDIN, когда нет данных, доступных из каналов или перенаправления, приведет к зависанию скрипта и ожиданию ввода STDIN.
Другие вещи, которые не работают
Пытаясь решить эту проблему, я рассмотрел несколько методов, которые не смогли решить проблему, в том числе:
- изучение переменных среды SSH
- использование
stat
файловых дескрипторов on / dev / stdin
- изучение интерактивного режима через
[[ "${-}" =~ 'i' ]]
- проверка состояния tty через
tty
иtty -s
- проверка
ssh
статуса через[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
Обратите внимание, что если вы используете ОС, которая поддерживает /proc
виртуальную файловую систему, вам, возможно, повезет, перейдя по символическим ссылкам для STDIO, чтобы определить, используется ли канал или нет. Однако /proc
это не кроссплатформенное POSIX-совместимое решение.
Я чрезвычайно заинтересован в решении этой проблемы, поэтому, пожалуйста, дайте мне знать, если вы думаете о любой другой технике, которая может работать, предпочтительно решениях на основе POSIX, которые работают как на Linux, так и на BSD.