Nie ma niezawodnego sposobu ustalenia, czy STDIN, STDOUT lub STDERR są przesyłane do / ze skryptu, głównie z powodu programów takich jak ssh
.
Rzeczy, które „normalnie” działają
Na przykład następujące rozwiązanie bash działa poprawnie w interaktywnej powłoce:
[[ -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'
Ale nie zawsze działają
Jednak podczas wykonywania tego polecenia jako polecenia innego niż TTY ssh
, strumienie STD zawsze wyglądają, jakby były przesyłane potokowo. Aby to zademonstrować, używając STDIN, ponieważ jest to łatwiejsze:
# 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 ${?}'
Dlaczego jest to ważne
Jest to dość duża sprawa, ponieważ sugeruje, że skrypt bash nie ma sposobu, aby stwierdzić, czy ssh
polecenie inne niż tty jest przesyłane potokowo, czy nie. Zauważ, że to niefortunne zachowanie zostało wprowadzone, gdy najnowsze wersje ssh
zaczęły używać potoków dla STDIO innych niż TTY. Wcześniejsze wersje używały gniazd, które MOGĄ być odróżniane od wewnątrz bash przy użyciu [[ -S ]]
.
Kiedy to ma znaczenie
To ograniczenie zwykle powoduje problemy, gdy chcesz napisać skrypt bash, który ma zachowanie podobne do skompilowanego narzędzia, takiego jak cat
. Na przykład cat
umożliwia następujące elastyczne zachowanie podczas obsługi różnych źródeł wejściowych jednocześnie i jest wystarczająco inteligentny, aby określić, czy odbiera dane wejściowe w potoku niezależnie od tego, czy ssh
używany jest tryb TTY czy wymuszony :
ssh -t localhost 'echo piped | cat - <( echo substituted )'
ssh -T localhost 'echo piped | cat - <( echo substituted )'
Możesz zrobić coś takiego tylko wtedy, gdy możesz wiarygodnie ustalić, czy rury są zaangażowane, czy nie. W przeciwnym razie wykonanie polecenia, które czyta STDIN, gdy żadne wejście nie jest dostępne ani z potoków, ani z przekierowania spowoduje zawieszenie skryptu i oczekiwanie na wejście STDIN.
Inne rzeczy, które nie działają
Próbując rozwiązać ten problem, przyjrzałem się kilku technikom, które nie rozwiązały problemu, w tym takim, które obejmują:
- badanie zmiennych środowiskowych SSH
- używając
stat
deskryptorów plików / dev / stdin
- badanie trybu interaktywnego przez
[[ "${-}" =~ 'i' ]]
- sprawdzanie statusu tty za pomocą
tty
itty -s
- sprawdzanie
ssh
statusu przez[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
Pamiętaj, że jeśli używasz systemu operacyjnego obsługującego /proc
wirtualny system plików, możesz mieć szczęście podążając za dowiązaniami symbolicznymi do STDIO, aby ustalić, czy potok jest używany, czy nie. Jednak /proc
nie jest wieloplatformowym rozwiązaniem zgodnym z POSIX.
Jestem niezwykle interesujący w rozwiązaniu tego problemu, więc proszę dać mi znać, jeśli pomyślisz o jakiejkolwiek innej technice, która może działać, najlepiej rozwiązaniach opartych na POSIX, które działają zarówno na Linuksie, jak i BSD.