Псевдо-терминал не будет выделен, потому что 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.
Per 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
будет передан обратно в захват интерполированной строки<<
начинается bash помощи heredoc'EOT'
указывает, что имя heredoc является EOT. Одинарные кавычки'
окружающий EOT указывает, что heredoc должен быть проанализирован как nowdoc, это особая форма heredoc, в которой содержимое не интерполируется bash, а передается в буквальном формателюбой контент, который находится между
<<'EOT'
и<newline>EOT<newline>
будет добавлен к выходу nowdocEOT
завершает nowdoc, в результате чего создается временный файл nowdoc и передается обратно вызывающему .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}"
Я добавляю этот ответ, потому что он решил связанную проблему, которую я имел с тем же сообщением об ошибке.
предупреждение Pseudo-terminal will not be allocated because stdin is not a terminal.
связано с тем, что ни одна команда не указана для ssh
в то время как stdin перенаправляется из документа here.
Из-за отсутствия указанной команды в качестве аргумента ssh
сначала ожидает интерактивный сеанс входа в систему (который потребует выделения pty на удаленном хосте), но затем должен понять, что его локальный stdin не является tty/pty. Перенаправление ssh
stdin из документа here обычно требует команды (например,/bin/sh
) указывается как аргумент ssh
- и в таком случае pty не будет выделен на удаленном хосте по умолчанию.
поскольку нет команд, которые должны выполняться через ssh
которые требуют наличия tty / pty (например,vim
или top
) the -t
переключатель ssh
это лишнее.
Просто используйте ssh -T user@server <<EOT ...
или ssh user@server /bin/bash <<EOT ...
и предупреждение исчезнет.
если <<EOF
не является экранированным или одинарным (i. e. <<\EOT
или <<'EOT'
) переменные внутри документа будет расширяется локальной оболочкой перед выполнением ssh ...
. Эффект заключается в том, что переменные внутри документа here останутся пустыми, поскольку они определены только в удаленной оболочке.
Итак, если $REL_DIR
должен быть как доступен локальной оболочкой, так и определен в удаленной оболочке,$REL_DIR
должен быть определен вне документа перед (Вариант 1 ниже); или, если <<\EOT
или <<'EOT'
используется, выход может быть присвоено REL_DIR
если единственный выход в stdout, genererated по 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, как правило, рецепт для проблем. Более надежным является использование стиля command-to-run-as-a-last-argument и передача скрипта в командной строке 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
в любом случае?
вся соответствующая информация находится в существующих ответах, но позвольте мне попробовать прагматичный резюме:
tl; dr:
-
передайте команды для запуска с помощью :
ssh jdoe@server '...'
-
'...'
строки могут охватывать несколько строк, поэтому вы можете сохранить свой код читаемым даже без использования вот-документ:ssh jdoe@server ' ... '
-
не передавайте команды через stdin, как в случае, когда вы используете здесь-документ:
ssh jdoe@server <<'EOF' # Do NOT do this ... EOF
передает команды в качестве аргумента работает как есть, и:
- проблема с псевдо-терминалом даже не возникнет.
-
, вам не нужно будет
exit
заявление в конце ваших команд, потому что сеанс автоматически завершится после обработки команд.
короче говоря: передача команд через stdin это механизм, который находится в противоречии с ssh
дизайн и вызывает проблемы, которые затем могут быть обойдены.
Читайте дальше, если хотите узнать больше.
дополнительно справочная информация:
ssh
's механизм для принятия команды для выполнения на целевом сервере - это : конечный операнд (аргумент non-option) принимает строку, содержащую одну или несколько команд оболочки.
по умолчанию команды выполняются без присмотра, внеинтерактивном shell, без использования (псевдо) терминала (опция
-T
подразумевается), и сеанс автоматически заканчивается когда заканчивается последняя команда обработка.-
в случае, если команды требуют взаимодействие с пользователем, например, отвечая на интерактивное приглашение, вы можете явно запросить создание pty (псевдо-tty), псевдо-терминал, который позволяет взаимодействовать с удаленным сеансом, используя , например:
ssh -t jdoe@server 'read -p "Enter something: "; echo "Entered: [$REPLY]"'
обратите внимание, что интерактивный
read
запрос работает только правильно с pty, так что-t
опция необходима.-
использование pty имеет заметный побочный эффект: stdout и stderr являются в сочетании и сообщили через стандартный вывод; другими словами: вы теряете различие между регулярным и выводом ошибок; например:
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
создает интерактивные shell - в том числе при отправке команд через stdin, где и начинается беда:
-
на интерактивные раковины,
ssh
обычно выделяет pty (псевдо-терминал) по умолчанию,за исключением если его stdin не подключен к (реальный) терминал.отправка команды через stdin означает, что
ssh
stdin больше не подключен к терминалу, поэтому нет pty создан, иssh
предупреждает вы соответственно:Pseudo-terminal will not be allocated because stdin is not a terminal.
-
даже
-t
опция, чьей целью являетсязапрос создание Пти, составляет не достаточно в этом случае: вы получите такое же предупреждение.немного странно, то вы должны двойной the
-t
опции форсировать создание Пти: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
, и обеспечить команды через stdin, заканчивается exit
команда, но обратите внимание, что если вам также нужны интерактивные функции, используя -tt
вместо -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. У тебя должен быть плинк шпатлевки.exe установлен для этого, чтобы работать.
решение
- M-x customize-variable (а затем нажмите Enter)
- tramp-default-method (а затем нажмите Войдите снова)
- в текстовом поле поставьте plink, а затем примените и сохраните буфер
- всякий раз, когда я пытаюсь получить доступ к удаленному серверу, я теперь использую C-x-f /user@host: а затем введите пароль. Соединение теперь правильно сделано под Emacs на Windows к моему удаленному серверу.