Как определить, работает ли мой сценарий оболочки через канал?

Как определить из сценария оболочки, отправляется ли его стандартный вывод на терминал или он передается другому процессу?

Показательный пример: я хотел бы добавить escape-коды для раскрашивания вывода, но только при интерактивном запуске, а не при передаче по конвейеру, аналогично тому, чтоls --color делает.

 Palec19 февр. 2014 г., 02:40
@ user940324 Правильная ссылкаserverfault.com/q/156470/197218
 user94032412 сент. 2011 г., 12:20
Вот еще несколько интересных тестовых случаев! <a href = "serverfault.com/questions/156470/... для сценария, который ожидает на stdin </a>

Ответы на вопрос(6)

какую оболочку вы используете, но в Bash вы можете сделать это:

#!/bin/bash

if [[ -t 1 ]]; then
    # stdout is a terminal
else
    # stdout is not a terminal
fi

Командаtest (встроенный вbash), имеет возможность проверить, является ли дескриптор файла tty.

if [ -t 1 ]; then
    # stdout is a tty
fi

Видеть "man test" или же "man bash"и искать"-t"

 Joel Purra25 нояб. 2015 г., 08:27
Смотрите также встроенный Bashhelp test (а такжеhelp help больше), тоinfo bash для получения более подробной информации. Эти команды хороши, если вы когда-нибудь заканчиваете писать сценарии в автономном режиме или просто хотите получить более широкое понимание.
 scy08 окт. 2013 г., 17:38
Как отметил FireFly в ответе dmckee, оболочка, которая не поддерживает -t, не соответствует POSIX.
 Neil Mayhew14 авг. 2013 г., 22:45
+1 для "man test", потому что / usr / bin / test будет работать даже в оболочке, которая не реализует -t в своем встроенном тесте

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

Однако при выполнении этой команды как не TTYssh команда, 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 не может определить, является ли не-ttyssh Команда передается по каналу или нет. Обратите внимание, что это неудачное поведение было введено, когда последние версииssh начал использовать трубы для не-TTY STDIO. В предыдущих версиях использовались сокеты, которые МОГУТ дифференцироваться изнутри bash с помощью[[ -S ]].

Когда это важно

Это ограничение обычно вызывает проблемы, когда вы хотите написать bash-скрипт, который ведет себя подобно скомпилированной утилите, такой какcat, Например,cat позволяет следующее гибкое поведение при одновременной обработке различных источников ввода и достаточно умен, чтобы определить, получает ли он ввод по каналу независимо от того, является ли он не TTY или принудительным TTYssh используется:

ssh -t localhost 'echo piped | cat - <( echo substituted )'
ssh -T localhost 'echo piped | cat - <( echo substituted )'

Вы можете сделать что-то подобное, только если сможете достоверно определить, задействованы ли трубы или нет. В противном случае выполнение команды, которая читает STDIN, когда нет ввода из каналов или перенаправления, приведет к зависанию скрипта и ожиданию ввода STDIN.

Другие вещи, которые не работают

Пытаясь решить эту проблему, я рассмотрел несколько методов, которые не смогли решить проблему, в том числе:

изучение переменных среды SSHс помощьюstat в / dev / stdin файловых дескрипторахизучение интерактивного режима через[[ "${-}" =~ 'i' ]]проверка статуса tty черезtty а такжеtty -sизученияssh статус через[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]

Обратите внимание, что если вы используете ОС, которая поддерживает/proc Виртуальная файловая система, вам может повезти, пройдя по символическим ссылкам для STDIO, чтобы определить, используется ли канал или нет. Тем не мение,/proc не является кроссплатформенным, POSIX-совместимым решением.

Я чрезвычайно заинтересован в решении этой проблемы, поэтому, пожалуйста, дайте мне знать, если вы думаете о какой-либо другой технике, которая могла бы работать, предпочтительно решениях на основе POSIX, которые работают как на Linux, так и на BSD.

Решение Вопроса

В чистой оболочке POSIX,

if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi

возвращает «терминал», потому что вывод отправляется на ваш терминал, тогда как

(if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi) | cat

возвращает "не терминал", потому что вывод скобок передаетсяcat.

-t флаг описывается на страницах руководства как

-t fd Истинно, если дескриптор файла fd открыт и ссылается на терминал.

... гдеfd может быть одним из обычных назначений дескриптора файла:

0:     stdin  
1:     stdout  
2:     stderr
 Palec27 янв. 2017 г., 11:26
Я согласен с тем, что после вашего редактирования (редакция 5) ответ будет более четким, чем в редакции 3, а также фактически верным (игнорирование того, что «возврат» используется очень неформально, где «отпечатки» будут более точными).
 FireFly14 мая 2013 г., 16:12
Чтобы уточнить,-t Флаг указывается в POSIX и поэтому должен работать для любой POSIX-совместимой оболочки (то есть это не расширение bash).pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
 dmckee14 мая 2012 г., 21:07
@Kelvin Фрагмент страницы руководства предполагает, что он должен, но эти файловые дескрипторы не назначены по умолчанию.
 Kelvin14 мая 2012 г., 21:04
Это работает с FD выше, чем 2?
 linux_newbie06 дек. 2016 г., 20:52
Работает и при запуске скрипта как удаленная команда ssh. Лучший ответ и очень простой.

Следующий код (проверено только в linux bash 4.4)не следует считать переносимым или рекомендованным, но для полноты вот это:

ls /proc/$/fdinfo/* >/dev/null 2>&1 || grep -q 'flags: 00

Я не знаю почему, но кажется, что файловый дескриптор "3" каким-то образом создается, когда функция bash имеет канал STDIN.

Надеюсь, это поможет,

/proc/$/fdinfo/0 && echo "pipe detected"

Я не знаю почему, но кажется, что файловый дескриптор "3" каким-то образом создается, когда функция bash имеет канал STDIN.

Надеюсь, это поможет,

отвечает как требуется.

bash_redir_test.sh выглядит так:

[[ -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'

В Linux это прекрасно работает:

:$ ./bash_redir_test.sh
STDOUT is attached to TTY

:$ ./bash_redir_test.sh | xargs echo
STDOUT is attached to a pipe

:$ rm bash_redir_test.log 
:$ ./bash_redir_test.sh >> bash_redir_test.log

:$ tail bash_redir_test.log 
STDOUT is attached to a redirection

На Солярисе:

:# ./bash_redir_test.sh
STDOUT is attached to TTY

:# ./bash_redir_test.sh | xargs echo
STDOUT is attached to a redirection

:# rm bash_redir_test.log 
bash_redir_test.log: No such file or directory

:# ./bash_redir_test.sh >> bash_redir_test.log
:# tail bash_redir_test.log 
STDOUT is attached to a redirection

:# 
 Dejay Clayton15 июн. 2015 г., 22:36
Интересно, я хотел бы иметь доступ к Solaris для тестирования. Если ваш экземпляр Solaris использует файловую систему "/ proc", существуют более надежные решения, которые включают поиск символических ссылок "/ proc" для stdin, stdout и stderr.

Ваш ответ на вопрос