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

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

ssh -t [email protected]<<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 [email protected]:$REL_DIR
scp ./dir2 [email protected]:$REL_DIR

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

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

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

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

Ответы

Ответ 1

Попробуйте 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.

Ответ 2

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

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

Ответ 3

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

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

ssh [email protected] /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 [email protected] /bin/bash
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT

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

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

ssh [email protected] "$( cat <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
)"

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

  • ssh [email protected] анализируется bash и интерпретируется как команда ssh, за которым следует аргумент [email protected], который должен быть передан команде ssh

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

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

  • 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 будет интерпретировать как удаленную команду для выполнения как [email protected]

Если вам нужно избегать использования внешних инструментов, таких как 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 [email protected] "${SSH_COMMAND}"

Ответ 4

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

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

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

Ответ 5

Предупреждающее сообщение 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 [email protected] <<EOT ... или ssh [email protected] /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 [email protected] "$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 [email protected]:"$REL_DIR"
scp -r ./dir2 [email protected]:"$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 [email protected]:"$REL_DIR"
scp -r ./dir2 [email protected]:"$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 [email protected]:"$REL_DIR"
scp -r ./dir2 [email protected]:"$REL_DIR"

Ответ 6

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

ssh [email protected] '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 в любом случае?

Ответ 7

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

TL;DR:

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

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

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

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

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


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

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

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

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

    • ssh -t [email protected] 'read -p "Enter something: "; echo "Entered: [$REPLY]"'

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

    • Использование pty имеет заметный побочный эффект: 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 [email protected] <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'


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

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

Ответ 8

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

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

ssh [email protected] /bin/bash <<'ENDSSH'
   hostname
ENDSSH

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

ssh [email protected]e <<'ENDSSH'
   hostname
ENDSSH

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

ssh [email protected] /bin/bash < run-command.sh

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

ssh [email protected] < run-command.sh

EXTRA

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

ssh -t [email protected] "$(<run-command.sh)"

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

ssh -t [email protected] "$(<run-command.sh)" | tee -a logfile.log

Ответ 9

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

Ответ 10

У меня была такая же ошибка в 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 на моем удаленном сервере.

Ответ 11

ssh -t foobar @localhost yourscript.pl