Не существует надежного способа определить, передаются ли 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.