Псевдотерминал не будет выделен, поскольку 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, в котором начинается проблема:
В маловероятном случае, когда команды, которые вы хотите передать в качестве аргумента, слишком длинны для вашей системы (если ее длина приближается к 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