Псевдо-терминал не будет выделен, потому что stdin не является терминалом

273

Я пытаюсь написать оболочку script, которая создает некоторые каталоги на удаленном сервере, а затем использует scp для копирования файлов с моей локальной машины на пульт. Вот что я до сих пор:

ssh -t user@server<<EOT
DEP_ROOT='/home/matthewr/releases'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR=$DEP_ROOT"/"$datestamp
if [ ! -d "$DEP_ROOT" ]; then
    echo "creating the root directory"
    mkdir $DEP_ROOT
fi
mkdir $REL_DIR
exit
EOT

scp ./dir1 user@server:$REL_DIR
scp ./dir2 user@server:$REL_DIR

Всякий раз, когда я запускаю его, я получаю это сообщение:

Pseudo-terminal will not be allocated because stdin is not a terminal.

И script просто висит навсегда.

Мой открытый ключ доверен на сервере, и я могу полностью запустить все команды за пределами script. Любые идеи?

  • 3
    Почему бы не использовать rsync?
  • 3
    Вы можете просто указать используемый терминал как ssh user@server /bin/bash <<EOT…
Показать ещё 2 комментария
Теги:
ssh

11 ответов

424
Лучший ответ

Попробуйте ssh -t -t (или ssh -tt для краткости), чтобы принудительно присвоить псевдо-tty, даже если stdin не является терминалом.

См. также: Завершение сеанса SSH, выполненного с помощью bash script

Из ssh manpage:

-T      Disable pseudo-tty allocation.

-t      Force pseudo-tty allocation.  This can be used to execute arbitrary 
        screen-based programs on a remote machine, which can be very useful,
        e.g. when implementing menu services.  Multiple -t options force tty
        allocation, even if ssh has no local tty.
  • 0
    Как вы можете видеть выше, я уже пытался -t. Я буду смотреть на перенаправление вывода, хотя.
  • 1
    Это предложение исправило мою проблему. У меня есть скрипт, вызывающий другой в фоновом режиме: «source myscript.sh &». Внутри «myscript.sh» есть ssh, который использует «sudo» следующим образом: ssh user @ host «sudo <command>». Использование одного параметра -t не помогло, только "-t -t".
Показать ещё 9 комментариев
134

Также с опцией -T из руководства

Отключить псевдо -T ty распределение

  • 9
    Этот ответ нуждается в большем количестве баллов - это правильный ответ, и не слишком длинный, как ответ от zanco, абсурдно, что «о, в любом случае, выделите TTY с -t -t», получает 107 баллов, когда вы можете просто пропустить его полностью
  • 1
    Да, люди должны использовать SO в качестве одного из источников информации, проверяйте работоспособность, обращаясь к справочным страницам и другой документации.
Показать ещё 8 комментариев
71

Per zanco answer, вы не предоставляете удаленную команду ssh, учитывая, как оболочка анализирует командную строку. Чтобы решить эту проблему, измените синтаксис вашего вызова команды ssh, чтобы удаленная команда состояла из синтаксически правильной многострочной строки.

Существует множество синтаксисов, которые можно использовать. Например, поскольку команды могут быть отправлены в bash и sh и, возможно, другие оболочки тоже, самое простое решение состоит в том, чтобы просто комбинировать вызов ssh оболочки с heredocs:

ssh user@server /bin/bash <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT

Обратите внимание, что выполнение вышеуказанного без /bin/bash приведет к предупреждению Pseudo-terminal will not be allocated because stdin is not a terminal. Также обратите внимание, что EOT окружен одинарными кавычками, так что bash распознает heredoc как nowdoc, отключив интерполяцию локальных переменных, чтобы текст команды был передан как есть - ssh.

Если вы являетесь поклонником труб, вы можете переписать вышеприведенное значение следующим образом:

cat <<'EOT' | ssh user@server /bin/bash
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT

То же предостережение о /bin/bash относится к указанному выше.

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

ssh user@server "$( cat <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
)"

Решение выше устраняет эту проблему следующим образом:

  • ssh user@server анализируется bash и интерпретируется как команда ssh, за которым следует аргумент user@server, который должен быть передан команде ssh

  • " начинается интерполированная строка, которая при завершении будет содержать аргумент, который будет передан команде ssh, которая в этом случае будет интерпретироваться ssh как удаленная команда для выполнения как user@server

  • $( начинает команду, которая должна быть выполнена, при этом вывод захватывается окруженной интерполированной строкой

  • cat - это команда для вывода содержимого любого файла. Вывод cat будет передан обратно в захваченную интерполированную строку

  • << начинается bash heredoc

  • 'EOT' указывает, что имя heredoc равно EOT. Одиночные кавычки ', окружающие EOT, указывают, что heredoc следует анализировать как nowdoc, что является специальной формой heredoc, в которой содержимое не получает интерполяцию с помощью bash, а скорее передается в буквальном формате

  • Любое содержимое, которое встречается между <<'EOT' и <newline>EOT<newline>, будет добавлено к выходу nowdoc

  • EOT завершает работу nowdoc, в результате создается временный файл nowdoc и передается обратно в вызывающую команду cat. cat выводит nowdoc и передает выход обратно в захваченную интерполированную строку

  • ) завершает выполнение команды

  • " завершает захваченную интерполированную строку. Содержимое интерполированной строки будет передано обратно в ssh как один аргумент командной строки, который ssh будет интерпретировать как удаленную команду для выполнения как user@server

Если вам нужно избегать использования внешних инструментов, таких как cat, и не против иметь два оператора вместо одного, используйте встроенный read с heredoc для генерации команды SSH:

IFS='' read -r -d '' SSH_COMMAND <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT

ssh user@server "${SSH_COMMAND}"
  • 1
    +1 год спустя! Это помогло мне в сценарии, над которым я работаю.
  • 0
    +1 полтора года спустя! :) действительно четкое объяснение. отлично сработано
Показать ещё 10 комментариев
46

Я добавляю этот ответ, потому что он решил связанную с этим проблему с тем же сообщением об ошибке.

Проблема: я установил cygwin под Windows и получил эту ошибку: Pseudo-terminal will not be allocated because stdin is not a terminal

Разрешение. Оказывается, что у меня была не клиентская программа и утилиты openssh. Из-за этого cygwin использовал версию ssh для Windows, а не версию cygwin. Решение заключалось в установке пакета openssh cygwin.

  • 9
    Для меня оказалось, что это была еще одна реализация ssh в PATH: $ which ssh/cygdrive/c/Program Files (x86)/Git/bin/ssh
  • 0
    и для установки openssh это может быть путь для Windows: superuser.com/a/301026/260710
Показать ещё 1 комментарий
29

Предупреждающее сообщение Pseudo-terminal will not be allocated because stdin is not a terminal. связано с тем, что для ssh не указана команда, а stdin перенаправляется из документа. Из-за отсутствия указанной команды в качестве аргумента ssh сначала ожидает интерактивный сеанс входа (который потребует выделения pty на удаленном хосте), но затем должен понять, что его локальный stdin не является tty/pty. Перенаправление ssh stdin из документа здесь обычно требует, чтобы команда (например, /bin/sh) указывалась как аргумент ssh - и в таком случае по умолчанию на удаленном хосте не будет назначено pty.

Поскольку для ssh нет команд, которые требуют наличия tty/pty (например, vim или top), переключение -t на ssh является излишним. Просто используйте ssh -T user@server <<EOT ... или ssh user@server /bin/bash <<EOT ..., и предупреждение исчезнет.

Если <<EOF не является экранированным или однокасканным (i. e. <<\EOT или <<'EOT'), переменные внутри документа здесь будут разворачиваться локальной оболочкой до того, как она выполнит ssh .... Эффект заключается в том, что переменные внутри документа здесь остаются пустыми, поскольку они определены только в удаленной оболочке.

Итак, если $REL_DIR должен быть доступен локальной оболочке и определен в удаленной оболочке, $REL_DIR должен быть определен вне документа здесь перед командой ssh (версия 1 > ниже); или, если используется <<\EOT или <<'EOT', вывод команды ssh может быть назначен REL_DIR, если единственный вывод команды ssh в stdout генерируется echo "$REL_DIR" внутри экранированного/документ с одним кавычками ( версия 2).

Третий вариант заключается в том, чтобы сохранить этот документ в переменной, а затем передать эту переменную в качестве аргумента команды в ssh -t user@server "$heredoc" (версия 3) ниже).

И последнее, но не менее важное: неплохо было бы проверить, были ли успешно созданы каталоги на удаленном хосте (см. проверить, существует ли файл на удаленном хосте с ssh).

# version 1

unset DEP_ROOT REL_DIR
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"

ssh localhost /bin/bash <<EOF
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
#echo "$REL_DIR"
exit
EOF

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"


# version 2

REL_DIR="$(
ssh localhost /bin/bash <<\EOF
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
exit
EOF
)"

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"


# version 3

heredoc="$(cat <<'EOF'
# -onlcr: prevent the terminal from converting bare line feeds to carriage return/line feed pairs
stty -echo -onlcr
DEP_ROOT='/tmp'
datestamp="$(date +%Y%m%d%H%M%S)"
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
stty echo onlcr
exit
EOF
)"

REL_DIR="$(ssh -t localhost "$heredoc")"

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"
  • 5
    Спасибо за очень подробное объяснение того, что означает эта ошибка и почему она появляется при использовании heredoc, это помогло мне понять ее, а не просто обойти ее.
21

Я не знаю, откуда происходит зависание, но перенаправление (или трубопровод) команд в интерактивный ssh ​​- это, в общем, рецепт проблем. Более разумно использовать стиль командной строки для запуска в качестве последнего аргумента и передать script в командной строке ssh:

ssh user@server 'DEP_ROOT="/home/matthewr/releases"
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR=$DEP_ROOT"/"$datestamp
if [ ! -d "$DEP_ROOT" ]; then
    echo "creating the root directory"
    mkdir $DEP_ROOT
fi
mkdir $REL_DIR'

(Все в одном гигантском аргументе командной строки ' -delimited).

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

Что вы пытались достичь с помощью -t в любом случае?

  • 1
    Опция -t была попыткой исправить проблему с терминалом Psuedo (она не работала). Я попробовал ваше решение, он избавился от штуковины терминала psuedo, но теперь он просто зависает ...
  • 4
    @Henning: Не могли бы вы уточнить или предоставить ссылку, касающуюся недостатков перенаправления или передачи в интерактивный ssh?
Показать ещё 1 комментарий
19

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

TL;DR:

  • ПРОВЕРИТЬ команды для запуска с использованием аргумента командной строки:
    ssh jdoe@server '...'

    • '...' строки могут охватывать несколько строк, поэтому вы можете сохранить свой код читаемым даже без использования здесь-документа:
      ssh jdoe@server ' ... '
  • НЕ передавайте команды через stdin, как в случае, когда вы используете here-document:
    ssh jdoe@server <<'EOF' # Do NOT do this ... EOF

Передача команд в качестве аргумента работает как-is и:

  • проблема с псевдотерминалом даже не возникнет.
  • вам не понадобится инструкция exit в конце ваших команд, потому что сеанс будет автоматически завершен после обработки команд.

Короче: передача команд через stdin - это механизм, который не соответствует дизайну ssh и вызывает проблемы, которые затем должны быть обработаны.
Продолжайте читать, если хотите узнать больше.


Дополнительная справочная информация:

ssh механизм принятия команд для выполнения на целевом сервере является аргументом командной строки: конечный операнд (необязательный аргумент) принимает строку, содержащую одну или несколько команд оболочки.

  • По умолчанию эти команды запускаются без присмотра в неинтерактивной оболочке без использования (псевдо) терминала (подразумевается опция -T), и сеанс автоматически завершается, когда последняя команда завершает обработку.

  • Если ваши команды требуют взаимодействия с пользователем, например, для ответа на интерактивное приглашение, вы можете явно запросить создание pty (псевдо -tty), псевдотерминал, который позволяет взаимодействовать с удаленным сеансом, используя параметр -T; например:.

    • ssh -t jdoe@server 'read -p "Enter something: "; echo "Entered: [$REPLY]"'

    • Обратите внимание, что интерактивная подсказка read работает корректно с помощью pty, поэтому требуется опция -T.

    • Использование pty имеет заметный побочный эффект: stdout и stderr объединены и оба передаются через stdout; другими словами: вы теряете различие между регулярным и выходным сигналом ошибки; например:

      • ssh jdoe@server 'echo out; echo err >&2' # OK - stdout and stderr separate

      • ssh -t jdoe@server 'echo out; echo err >&2' # !! stdout + stderr -> stdout

В отсутствие этого аргумента ssh создает интерактивную оболочку - в том числе, когда вы отправляете команды через stdin, в котором начинается проблема:

  • Для интерактивной оболочки ssh по умолчанию обычно назначает pty (псевдотерминал), за исключением того, что его stdin не подключен к (реальному) терминалу.

    • Отправка команд через stdin означает, что ssh stdin больше не подключен к терминалу, поэтому нет pty, и ssh предупреждает вас соответственно:
      Pseudo-terminal will not be allocated because stdin is not a terminal.

    • Даже вариант -T, чья явная цель - запросить создание pty, в этом случае не достаточно: вы получите то же предупреждение.

      • Несколько любопытно, что вы должны удвоить опцию -T, чтобы заставить создание pty: ssh -t -t ... или ssh -tt ... показать, что вы действительно это действительно имеете в виду.

      • Возможно, обоснование необходимости этого очень преднамеренного шага состоит в том, что вещи могут работать не так, как ожидалось. Например, на macOS 10.12 кажущийся эквивалент указанной выше команды, предоставляющей команды через stdin и используя -tt, не работает должным образом; сеанс застревает после ответа на приглашение read:
        ssh -tt jdoe@server <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'


В маловероятном случае, когда команды, которые вы хотите передать в качестве аргумента, слишком длинны для вашей системы (если ее длина приближается к getconf ARG_MAX - см. в этой статье), сначала скопируйте код в удаленную систему в виде script (используя, например, scp), а затем отправьте команду для выполнения этого script.

В крайнем случае используйте -T и предоставьте команды через stdin с помощью команды exit, но обратите внимание, что если вам также нужны интерактивные функции, использование -tt вместо -T может не работать.

  • 1
    Это работало отлично, способ -tt заставлял терминал отображать каждую команду, отправленную через ssh.
  • 1
    «Удвоение опции -t для принудительного создания pty: ssh -t -t ... или ssh -tt ... показывает, что вы действительно, действительно это имеете в виду». - ха, ха - спасибо, это смешно, но тоже серьезно - я не могу разобраться с двойным т, все, что я знаю, это то, что если я вставлю один т, это не сработает
5

После прочтения многих этих ответов я решил поделиться своим решением. Все, что я добавил, это /bin/bash перед heredoc, и он больше не дает ошибку.

Используйте это:

ssh user@machine /bin/bash <<'ENDSSH'
   hostname
ENDSSH

Вместо этого (дает ошибку):

ssh user@machine <<'ENDSSH'
   hostname
ENDSSH

Или используйте это:

ssh user@machine /bin/bash < run-command.sh

Вместо этого (дает ошибку):

ssh user@machine < run-command.sh

EXTRA

Если вам по-прежнему требуется удаленное интерактивное приглашение, например. если script вы запускаете удаленно, вы запрашиваете пароль или другую информацию, потому что предыдущие решения не позволят вам вводить подсказки.

ssh -t user@machine "$(<run-command.sh)"

И если вы также хотите зарегистрировать весь сеанс в файле logfile.log:

ssh -t user@machine "$(<run-command.sh)" | tee -a logfile.log
1

У меня была такая же проблема в cygwin. Установка openssh в cygwin решает проблему лучше всего. Запуск на Windows ssh -t -t печатает текущую команду (перед stdout), которая мне не нравится.

0

У меня была такая же ошибка в Windows, используя emacs 24.5.1 для подключения к некоторым серверам компании через /ssh: user @host. Я решил, что проблема заключается в установке переменной "tramp-default-method" для "plink", и всякий раз, когда я подключаюсь к серверу, я опускаю протокол ssh. Для этого вам необходимо установить PuTTY plink.exe.

Решение

  • M-x customize-variable (а затем нажмите Enter)
  • tramp-default-method (а затем снова нажмите Enter)
  • В текстовом поле введите plink, а затем Apply and Save the buffer
  • Всякий раз, когда я пытаюсь получить доступ к удаленному серверу, теперь я использую C-x-f/user @host: а затем введите пароль. Теперь соединение корректно выполняется в Emacs на Windows на моем удаленном сервере.
-2

ssh -t foobar @localhost yourscript.pl

  • 0
    ОП также использует -t , но в их конкретном сценарии этого недостаточно, что побудило начать вопрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню