Pseudo-terminal tidak akan dialokasikan karena stdin bukan terminal


345

Saya mencoba menulis skrip shell yang membuat beberapa direktori di server jauh dan kemudian menggunakan scp untuk menyalin file dari mesin lokal saya ke remote. Inilah yang saya miliki sejauh ini:

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

Setiap kali saya menjalankannya saya menerima pesan ini:

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

Dan naskahnya hang selamanya.

Kunci publik saya dipercaya di server dan saya bisa menjalankan semua perintah di luar skrip dengan baik. Ada ide?


4
Anda cukup menentukan terminal yang akan digunakan sepertissh user@server /bin/bash <<EOT…
Buzut

3
@Buzut: Anda mungkin bermaksud shell , tetapi, ya, menetapkan /bin/bashsecara eksplisit adalah salah satu cara untuk menghindari masalah.
mklement0

1
@ mklement0 memang, itulah yang saya maksud. Terima kasih untuk memperbaiki itu;)
Buzut

Jawaban:


512

Coba ssh -t -t(atau ssh -ttsingkatnya) untuk memaksakan alokasi pseudo-tty bahkan jika stdin bukan terminal.

Lihat juga: Mengakhiri sesi SSH yang dijalankan oleh skrip bash

Dari 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.

21
Saya mengalami masalah serupa dalam skrip yang dijalankan di sini. Saya menambahkan -t -t tetapi sekarang saya mendapatkan kesalahan baru. "tcgetattr: ioctl tidak sesuai untuk perangkat"
MasterZ

5
Kenapa ssh -t -tdan tidak ssh -tt? Apakah ada perbedaan yang tidak saya sadari?
Jack

3
@MasterZ Hal yang sama di sini. Akan menyenangkan untuk mendapatkan jawaban untuk iniInappropriate IOCtl for device
krb686

11
@ Jack -ttdan -t -tsetara; menentukan argumen secara terpisah atau dihancurkan bersama-sama menjadi masalah gaya / preferensi pribadi, dan ketika sampai pada itu, ada argumen yang valid untuk melakukannya. tapi sungguh, itu hanya preferensi pribadi.
JDS

5
Apa artinya ketika dikatakan "bahkan jika ssh tidak memiliki tty lokal"?
CMCDragonkai

192

Juga dengan opsi -Tdari manual

Nonaktifkan alokasi pseudo-tty


11
Jawaban ini membutuhkan lebih banyak poin - ini adalah jawaban yang tepat, dan tidak terlalu lama seperti jawaban oleh zanco, tidak masuk akal bahwa "oh, alokasikan TTY dengan -t -t" mendapat 107 poin, ketika Anda bisa melewatkannya sama sekali
nhed

2
Baru saja terbalik; itu memang bekerja lebih baik untuk saya daripada -t -t
Vincent Hiribarren

15
'-t -t' berfungsi, namun dengan '-T' i get 'sudo: maaf, Anda harus memiliki tty untuk menjalankan sudo'
Ivan Balashov

3
@nhed: -ttOpsi muncul paling baik bagi kita yang benar-benar menginginkan TTY dan mendapatkan pesan kesalahan OP. Ini -Tjawabannya adalah lebih baik jika Anda tidak perlu TTY tersebut.
ErichBSchulz

3
@nhed - yakin - tapi saya yakin orang lain seperti saya tiba di sini dari Googling kesalahan dalam pesan kesalahan Tampaknya tidak masuk akal untuk menjelaskan bagaimana googler lain harus memilih antara 2 jawaban yang bersaing. atau mungkin tidak. saya tidak tahu
ErichBSchulz

91

Per jawaban zanco , Anda tidak menyediakan perintah jarak jauh ssh, mengingat bagaimana shell mem-parsing baris perintah. Untuk mengatasi masalah ini, ubah sintaks sshpermintaan perintah Anda sehingga perintah jarak jauh terdiri dari string multi-line yang benar secara sintaksis.

Ada berbagai sintaks yang bisa digunakan. Misalnya, karena perintah dapat disalurkan ke bashdan sh, dan mungkin juga shell lain, solusi paling sederhana adalah dengan hanya menggabungkan sshpermintaan shell dengan heredocs:

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

Perhatikan bahwa mengeksekusi tanpa di atas /bin/bashakan menghasilkan peringatan Pseudo-terminal will not be allocated because stdin is not a terminal. Perhatikan juga yang EOTdikelilingi oleh tanda kutip tunggal, sehingga bashmengenali heredoc sebagai nowdoc , mematikan interpolasi variabel lokal sehingga teks perintah akan diteruskan apa adanya ssh.

Jika Anda seorang penggemar pipa, Anda dapat menulis ulang di atas sebagai berikut:

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

Peringatan yang sama tentang /bin/bashberlaku untuk di atas.

Pendekatan lain yang valid adalah untuk meneruskan perintah jarak jauh multi-line sebagai string tunggal, menggunakan beberapa lapisan bashinterpolasi variabel sebagai berikut:

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

Solusi di atas memperbaiki masalah ini dengan cara berikut:

  1. ssh user@serverdiuraikan oleh bash, dan ditafsirkan sebagai sshperintah, diikuti oleh argumen user@serveruntuk diteruskan ke sshperintah

  2. "memulai string interpolasi, yang ketika selesai, akan terdiri dari argumen untuk diteruskan ke sshperintah, yang dalam hal ini akan ditafsirkan dengan sshmenjadi perintah jarak jauh untuk dieksekusi sebagaiuser@server

  3. $( memulai perintah yang akan dieksekusi, dengan output yang ditangkap oleh string interpolasi sekitarnya

  4. catadalah perintah untuk menampilkan isi file apa pun yang mengikuti. Output dari catakan diteruskan kembali ke dalam string penangkap interpolasi

  5. <<memulai bash heredoc

  6. 'EOT'menetapkan bahwa nama heredoc adalah EOT. Kutipan tunggal 'seputar EOT menentukan bahwa heredoc harus diuraikan sebagai nowdoc , yang merupakan bentuk khusus dari heredoc di mana isinya tidak diinterpolasi oleh bash, tetapi diteruskan dalam format literal

  7. Konten apa pun yang ditemui di antara <<'EOT'dan <newline>EOT<newline>akan ditambahkan ke output nowdoc

  8. EOTmengakhiri nowdoc, menghasilkan file sementara nowdoc dibuat dan diteruskan kembali ke catperintah panggilan . catoutput nowdoc dan meneruskan output kembali ke string interpolasi menangkap

  9. ) menyimpulkan perintah yang akan dieksekusi

  10. "menyimpulkan string yang diinterpolasi menangkap. Isi dari string yang diinterpolasi akan dikembalikan sshsebagai argumen baris perintah tunggal, yang sshakan ditafsirkan sebagai perintah jarak jauh untuk dieksekusi sebagaiuser@server

Jika Anda perlu menghindari menggunakan alat eksternal seperti cat, dan tidak keberatan memiliki dua pernyataan, bukan satu, gunakan readbuilt-in dengan heredoc untuk menghasilkan perintah 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 satu setengah tahun kemudian! :) penjelasannya sangat jelas. dilakukan dengan baik
yaroslavTir

1
Bagi saya (dan saya tidak berarti orang "DevOps") inilah yang berhasil (saya menggunakan Jenkins). Saya juga mencoba saran "-t -t" dan "-T", tetapi mengalami masalah aliran terbuka dan masalah non-eksekusi.
Ryan Crews

2 tahun kemudian, sangat membantu
jack-nie

+1 Penjelasan luar biasa. Tetapi jika Anda membuat potongan kode terakhir (IFS = '' *) sebuah fungsi untuk menjalankan perintah jarak jauh, bagaimana Anda akan mengirimkan $ 1 ke IFS = '' *? Tampaknya docent mengenali konten $ 1 sama sekali.
Cristian Matthias Ambæk

1
@ mklement0 Tentu, Anda dapat keluar dari string yang diperlukan, tetapi siapa yang mau repot harus memodifikasi pernyataan skrip yang mungkin Anda salin dan tempel dari skrip shell lain, atau stackoverflow untuk masalah ini? Juga, keanggunan catsolusi adalah bahwa hal itu dicapai dalam pernyataan tunggal (walaupun majemuk), dan tidak menghasilkan variabel sementara yang mencemari lingkungan shell.
Dejay Clayton

63

Saya menambahkan jawaban ini karena itu memecahkan masalah terkait yang saya alami dengan pesan kesalahan yang sama.

Masalah : Saya telah menginstal cygwin di Windows dan mendapatkan kesalahan ini:Pseudo-terminal will not be allocated because stdin is not a terminal

Penyelesaian : Ternyata saya belum menginstal program dan utilitas klien openssh. Karena itu cygwin menggunakan implementasi ssh Windows, bukan versi cygwin. Solusinya adalah menginstal paket openssh cygwin.


10
Bagi saya ternyata itu adalah implementasi ssh lain dalam PATH:$ which ssh/cygdrive/c/Program Files (x86)/Git/bin/ssh
FelixJongleur42

dan untuk menginstal opensshini mungkin cara yang tepat untuk Windows: superuser.com/a/301026/260710
Andreas Dietrich

Solusi ini bekerja untuk saya. Setelah menginstal openssh, masalah hilang.
jdhao

34

Pesan peringatan Pseudo-terminal will not be allocated because stdin is not a terminal.adalah karena fakta bahwa tidak ada perintah yang ditentukan untuk sshsementara stdin diarahkan dari dokumen di sini. Karena kurangnya perintah yang ditentukan sebagai argumen sshpertama mengharapkan sesi login interaktif (yang akan membutuhkan alokasi pty pada host jarak jauh) tetapi kemudian harus menyadari bahwa stdin lokalnya adalah no tty / pty. Mengarahkan sshstdin dari dokumen di sini biasanya memerlukan perintah (seperti /bin/sh) untuk ditetapkan sebagai argumen untuk ssh- dan dalam kasus seperti itu tidak ada pty yang akan dialokasikan pada host jarak jauh secara default.

Karena tidak ada perintah yang harus dieksekusi melalui sshyang membutuhkan kehadiran tty / pty (seperti vimatau top) -tberalih ke sshberlebihan. Cukup gunakan ssh -T user@server <<EOT ...atau ssh user@server /bin/bash <<EOT ...dan peringatan akan hilang.

Jika <<EOFtidak lolos atau kutip tunggal (yaitu <<\EOTatau <<'EOT') variabel di dalam dokumen di sini akan diperluas oleh shell lokal sebelum dieksekusi ssh .... Efeknya adalah bahwa variabel-variabel di dalam dokumen di sini akan tetap kosong karena mereka hanya didefinisikan di shell jauh.

Jadi, jika $REL_DIRharus dapat diakses oleh shell lokal dan didefinisikan dalam shell jauh, $REL_DIRharus didefinisikan di luar dokumen di sini sebelum sshperintah ( versi 1 di bawah); atau, jika <<\EOTatau <<'EOT'digunakan, output dari sshperintah dapat ditugaskan ke REL_DIRjika satu-satunya output dari sshperintah untuk stdout dihasilkan oleh echo "$REL_DIR"di dalam dokumen di sini lolos / dikutip tunggal ( versi 2 di bawah).

Opsi ketiga adalah menyimpan dokumen di sini dalam sebuah variabel dan kemudian meneruskan variabel ini sebagai argumen perintah ke ssh -t user@server "$heredoc"( versi 3 di bawah).

Dan, last but not least, bukan ide yang buruk untuk memeriksa apakah direktori pada host jarak jauh berhasil dibuat (lihat: periksa apakah file ada di host jarak jauh dengan 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
Terima kasih atas penjelasan yang sangat terperinci tentang apa arti kesalahan ini dan mengapa itu muncul ketika menggunakan heredoc, ini membantu saya untuk benar-benar memahaminya daripada hanya mengatasinya.
Dan

29

Semua informasi yang relevan ada di jawaban yang ada, tetapi izinkan saya mencoba ringkasan pragmatis :

tl; dr:

  • Lewati perintah untuk dijalankan menggunakan argumen baris perintah :
    ssh jdoe@server '...'

    • '...' string dapat menjangkau beberapa baris, sehingga Anda dapat membuat kode Anda dapat dibaca bahkan tanpa menggunakan dokumen di sini:
      ssh jdoe@server ' ... '
  • JANGAN melewati perintah melalui stdin , seperti halnya ketika Anda menggunakan dokumen-sini :
    ssh jdoe@server <<'EOF' # Do NOT do this ... EOF

Melewati perintah sebagai argumen berfungsi apa adanya, dan:

  • masalah dengan pseudo-terminal bahkan tidak akan muncul.
  • Anda tidak memerlukan exitpernyataan di akhir perintah, karena sesi akan keluar secara otomatis setelah perintah diproses.

Singkatnya: meneruskan perintah melalui stdin adalah mekanisme yang bertentangan dengan sshdesain dan menyebabkan masalah yang kemudian harus diselesaikan.
Baca terus, jika Anda ingin tahu lebih banyak.


Informasi latar belakang opsional:

sshMekanisme untuk menerima perintah untuk dieksekusi pada server target adalah argumen baris perintah : operan terakhir (argumen non-opsi) menerima string yang berisi satu atau lebih perintah shell.

  • Secara default, perintah-T ini berjalan tanpa pengawasan, dalam shell non-interaktif , tanpa menggunakan terminal (semu) (opsi tersirat), dan sesi secara otomatis berakhir ketika perintah terakhir selesai diproses.

  • Jika perintah Anda memerlukan interaksi pengguna , seperti merespons permintaan interaktif, Anda dapat secara eksplisit meminta pembuatan pty (pseudo-tty) , terminal pseudo, yang memungkinkan berinteraksi dengan sesi jarak jauh, menggunakan -topsi; misalnya:

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

    • Perhatikan bahwa readprompt interaktif hanya berfungsi dengan benar dengan pty, sehingga -topsi diperlukan.

    • Menggunakan pty memiliki efek samping yang mencolok: stdout dan stderr digabungkan dan keduanya dilaporkan melalui stdout ; dengan kata lain: Anda kehilangan perbedaan antara output reguler dan kesalahan; misalnya:

      • 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

Dengan tidak adanya argumen ini, sshbuat shell interaktif - termasuk ketika Anda mengirim perintah melalui stdin , yang merupakan awal masalah:

  • Untuk shell interaktif , sshbiasanya mengalokasikan pty (pseudo-terminal) secara default, kecuali jika stdinnya tidak terhubung ke terminal (nyata).

    • Mengirim perintah melalui stdin berarti sshstdin tidak lagi terhubung ke terminal, jadi tidak ada pty yang dibuat, dan ssh memperingatkan Anda :
      Pseudo-terminal will not be allocated because stdin is not a terminal.

    • Bahkan -topsi, yang tujuan utamanya adalah untuk meminta pembuatan pty, tidak cukup dalam hal ini : Anda akan mendapatkan peringatan yang sama.

      • Agak anehnya, Anda kemudian harus dua kali lipat pada -tpilihan untuk penciptaan kekuatan sebuah pty: ssh -t -t ...atau ssh -tt ...menunjukkan bahwa Anda benar-benar, benar-benar serius .

      • Mungkin alasan untuk memerlukan langkah yang disengaja ini adalah bahwa hal - hal mungkin tidak berfungsi seperti yang diharapkan . Sebagai contoh, pada macOS 10.12, padanan yang tampak dari perintah di atas, memberikan perintah melalui stdin dan menggunakan -tt, tidak bekerja dengan baik; sesi macet setelah menanggapi readprompt:
        ssh -tt jdoe@server <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'


Jika perintah yang ingin Anda sampaikan sebagai argumen membuat baris perintah terlalu panjang untuk sistem Anda (jika panjangnya mendekati getconf ARG_MAX- lihat artikel ini ), pertimbangkan menyalin kode ke sistem jarak jauh dalam bentuk skrip terlebih dahulu ( menggunakan, misalnya, scp), dan kemudian mengirim perintah untuk menjalankan skrip itu.

Dalam keadaan darurat, gunakan -T, dan berikan perintah melalui stdin , dengan exitperintah tambahan, tetapi perhatikan bahwa jika Anda juga membutuhkan fitur interaktif, penggunaan -ttsebagai pengganti -Tmungkin tidak berfungsi.


1
Ini bekerja dengan sempurna, cara -tt membuat terminal menampilkan setiap perintah yang dikirim melalui ssh.
Mark E

1
"gandakan opsi -t untuk memaksa pembuatan pty: ssh -t -t ... atau ssh -tt ... menunjukkan bahwa Anda benar-benar bersungguh-sungguh." - ha, ha - terima kasih itu lucu tapi serius juga - Saya tidak bisa mendapatkan kepalaku sekitar t semua yang saya tahu adalah jika saya memasukkan satu t itu tidak wrok
DavidC

22

Saya tidak tahu dari mana hang itu berasal, tetapi mengarahkan ulang (atau menyalurkan) perintah ke ssh interaktif secara umum merupakan resep untuk masalah. Lebih kuat untuk menggunakan gaya perintah-untuk-menjalankan-sebagai-argumen-terakhir dan meneruskan skrip pada baris perintah 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'

(Semua dalam satu 'argumen baris perintah multiline -delimited raksasa ).

Pesan pseudo-terminal adalah karena Anda -tyang meminta ssh untuk mencoba membuat lingkungan yang dijalankan pada mesin jarak jauh terlihat seperti terminal aktual untuk program yang berjalan di sana. Klien ssh Anda menolak untuk melakukan itu karena itu input standarnya sendiri bukan terminal, sehingga tidak memiliki cara untuk meneruskan API terminal khusus dan seterusnya dari mesin jarak jauh ke terminal Anda yang sebenarnya di ujung lokal.

Lagi pula, apa yang ingin Anda capai -t?


1
Opsi -t adalah upaya untuk memperbaiki masalah terminal Psuedo (tidak berhasil). Saya mencoba solusi Anda, itu menyingkirkan hal terminal psuedo tapi sekarang hanya hang ...
Matius

4
@Henning: Bisakah Anda menjabarkan atau memberikan tautan tentang kerugian pengalihan atau perpipaan ke ssh interaktif?
Isaac Kleinman

agak terlambat tetapi: di sini Anda tidak perlu terminal palsu, jadi gunakan opsi -Tsebagai gantinya.
Florian Castellane

6

Setelah membaca banyak jawaban ini saya pikir saya akan membagikan solusi yang dihasilkan. Yang saya tambahkan adalah/bin/bash sebelum heredoc dan tidak memberikan kesalahan lagi.

Gunakan ini:

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

Alih-alih ini (memberikan kesalahan):

ssh user@machine <<'ENDSSH'
   hostname
ENDSSH

Atau gunakan ini:

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

Alih-alih ini (memberikan kesalahan):

ssh user@machine < run-command.sh

EKSTRA :

Jika Anda masih menginginkan prompt interaktif jarak jauh mis. Jika skrip yang Anda jalankan dari jarak jauh meminta kata sandi atau informasi lainnya, karena solusi sebelumnya tidak memungkinkan Anda untuk mengetikkan prompt.

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

Dan jika Anda juga ingin mencatat seluruh sesi dalam file logfile.log:

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

0

Saya mengalami kesalahan yang sama di bawah Windows menggunakan emacs 24.5.1 untuk terhubung ke beberapa server perusahaan melalui / ssh: user @ host. Apa yang memecahkan masalah saya adalah menyetel variabel "tramp-default-method" menjadi "plink" dan setiap kali saya terhubung ke server saya mengabaikan protokol ssh. Anda perlu menginstal plink.exe Putty agar ini berfungsi.

Larutan

  1. Mx kustom-variabel (lalu tekan Enter)
  2. tramp-default-method (lalu tekan Enter lagi)
  3. Pada bidang teks, masukkan plink, lalu Terapkan dan Simpan buffer
  4. Setiap kali saya mencoba mengakses server jarak jauh saya sekarang menggunakan Cxf / user @ host: dan kemudian masukkan kata sandi. Koneksi sekarang dibuat dengan benar di bawah Emacs pada Windows ke server jarak jauh saya.

-3

ssh -t foobar @ localhost yourscript.pl


2
OP -tjuga menggunakan , tetapi dalam skenario spesifik mereka itu tidak cukup, yang mendorong pertanyaan untuk memulai.
mklement0
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.