Псевдо-терминал не будет выделен, потому что stdin не является терминалом
Я пытаюсь написать сценарий оболочки, который создает некоторые каталоги на удаленном сервере, а затем использует 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.
И сценарий просто висит навсегда.
Мой открытый ключ является доверенным на сервере, и я могу отлично выполнить все команды вне сценария. Есть идеи?
10 ответов
Пытаться ssh -t -t
(или же ssh -tt
для краткости) для принудительного выделения псевдо-tty, даже если stdin не является терминалом.
Смотрите также: Завершение сеанса SSH, выполняемого сценарием bash.
Из 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.
Также с опцией -T
из руководства
Отключить псевдо-tty распределение
Согласно ответу zanco, вы не предоставляете удаленную команду 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
будет передан обратно в захватываемую интерполированную строку<<
начинается Баш Эредок'EOT'
указывает, что имя heredoc - EOT. Одинарные кавычки'
окружающий EOT указывает, что heredoc должен быть проанализирован как nowdoc, который представляет собой особую форму heredoc, в которой содержимое не интерполируется bash, а передается в буквальном форматеЛюбой контент, который встречается между
<<'EOT'
а также<newline>EOT<newline>
будет добавлен к выводу nowdocEOT
завершает 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}"
Я добавляю этот ответ, потому что он решил проблему, связанную с тем же сообщением об ошибке.
Проблема: я установил Cygwin под Windows и получал эту ошибку: Pseudo-terminal will not be allocated because stdin is not a terminal
Решение: Оказывается, я не установил клиентскую программу и утилиты openssh. Из-за этого cygwin использовал реализацию ssh для Windows, а не версию cygwin. Решением было установить пакет openssh cygwin.
Вся соответствующая информация содержится в существующих ответах, но позвольте мне сделать прагматическое резюме:
ТЛ; др:
Передайте команды для запуска, используя аргумент командной строки:
ssh jdoe@server '...'
'...'
Строки могут занимать несколько строк, поэтому вы можете сохранять код читабельным даже без использования документа здесь:ssh jdoe@server ' ... '
НЕ передавайте команды через stdin, как в случае использования здесь-документа:
ssh jdoe@server <<'EOF' # Do NOT do this ... EOF
Передача команд в качестве аргумента работает как есть, и:
- проблема с псевдотерминалом даже не возникнет.
- вам не понадобится
exit
оператор в конце ваших команд, потому что сеанс автоматически завершится после того, как команды были обработаны.
Вкратце: передача команд через stdin - это механизм, который противоречит ssh
Это дизайн и вызывает проблемы, которые должны быть решены.
Читайте дальше, если вы хотите узнать больше.
Дополнительная справочная информация:
ssh
Механизм принятия команд для выполнения на целевом сервере является аргументом командной строки: последний операнд (не опциональный аргумент) принимает строку, содержащую одну или несколько команд оболочки.
По умолчанию эти команды выполняются автоматически в неинтерактивной оболочке без использования (псевдо) терминала (опция
-T
подразумевается), и сеанс автоматически заканчивается, когда последняя команда заканчивает обработку.В случае, если ваши команды требуют взаимодействия с пользователем, такого как ответ на интерактивное приглашение, вы можете явно запросить создание pty (pseudo-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 (псевдотерминал), за исключением случаев, когда его стандартный ввод не подключен к (реальному) терминалу.Отправка команд через стандартный ввод означает, что
ssh
sdin больше не подключен к терминалу, поэтому 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
- см. эту статью), попробуйте сначала скопировать код в удаленную систему в форме сценария (например, используя scp
), а затем отправьте команду для выполнения этого сценария.
В крайнем случае, используйте -T
и предоставьте команды через стандартный ввод, с завершающим exit
команда, но обратите внимание, что если вам также нужны интерактивные функции, используя -tt
вместо -T
может не работать
Предупреждающее сообщение Pseudo-terminal will not be allocated because stdin is not a terminal.
связано с тем, что не указана команда для ssh
в то время как стандартный ввод перенаправляется из документа здесь. Из-за отсутствия указанной команды в качестве аргумента ssh
сначала ожидает интерактивный сеанс входа в систему (который потребует выделения pty на удаленном хосте), но затем должен понять, что его локальный stdin не является tty / pty. Перенаправление ssh
Для ввода стандартного документа из этого документа обычно требуется команда (например, /bin/sh
) быть указанным в качестве аргумента ssh
- и в этом случае pty не будет выделяться на удаленном хосте по умолчанию.
Поскольку нет команд, которые должны быть выполнены с помощью ssh
которые требуют наличия tty / pty (например, vim
или же top
) -t
переключиться на ssh
это лишнее. Просто используйте ssh -T user@server <<EOT ...
или же ssh user@server /bin/bash <<EOT ...
и предупреждение уйдет.
Если <<EOF
не экранирован или не заключен в одинарные кавычки (т.е. <<\EOT
или же <<'EOT'
) переменные внутри документа здесь будут расширены локальной оболочкой перед выполнением ssh ...
, В результате переменные внутри документа здесь останутся пустыми, поскольку они определены только в удаленной оболочке.
Так что если $REL_DIR
должен быть доступен как локальной оболочкой, так и определен в удаленной оболочке, $REL_DIR
должен быть определен вне документа здесь до ssh
команда (версия 1 ниже); или если <<\EOT
или же <<'EOT'
используется, выход из ssh
команда может быть назначена REL_DIR
если единственный выход ssh
Команда на стандартный вывод генерируется echo "$REL_DIR"
внутри экранированного / цитируемого здесь документа (версия 2 ниже).
Третий вариант - сохранить документ here в переменной, а затем передать эту переменную в качестве аргумента команды 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"
Я не знаю, откуда происходит зависание, но перенаправление (или передача) команд в интерактивный ssh - это вообще рецепт проблем. Более надежно использовать стиль "команда для запуска как последний аргумент" и передать сценарий в командной строке 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'
(Все в одном гиганте '
многострочный аргумент командной строки).
Псевдо-терминал сообщение из-за вашего -t
который просит ssh сделать так, чтобы окружение, которое он запускает на удаленной машине, выглядело как действительный терминал для программ, которые там работают. Ваш ssh-клиент отказывается делать это, потому что его собственный стандартный ввод не является терминалом, поэтому он не может передавать специальные API-интерфейсы терминала с удаленного компьютера на ваш реальный терминал на локальном конце.
Чего вы пытались достичь с -t
тем не мение?
Прочитав много ответов, я решил поделиться своим решением. Все, что я добавил, это /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
ДОПОЛНИТЕЛЬНО:
Если вам по-прежнему нужна удаленная интерактивная подсказка, например, если сценарий, который вы запускаете удаленно, запрашивает у вас пароль или другую информацию, потому что предыдущие решения не позволяли вам вводить подсказки.
ssh -t user@machine "$(<run-command.sh)"
И если вы также хотите войти весь сеанс в файл logfile.log
:
ssh -t user@machine "$(<run-command.sh)" | tee -a logfile.log
У меня была такая же ошибка в Windows, когда я использовал emacs 24.5.1 для соединения с некоторыми серверами компании через /ssh:user@host. Что решило мою проблему, так это установив переменную "tramp-default-method" в "plink", и всякий раз, когда я подключаюсь к серверу, я пропускаю протокол ssh. Вы должны иметь установленный в PuTTY файл plink.exe, чтобы это работало.
Решение
- Mx customize-variable (а затем нажмите Enter)
- tramp-default-method (а затем снова нажмите Enter)
- В текстовое поле положить plink, а затем применить и сохранить буфер
- Всякий раз, когда я пытаюсь получить доступ к удаленному серверу, я теперь использую Cxf /user@host: и затем вводю пароль. Теперь в Emacs в Windows правильно установлено соединение с моим удаленным сервером.