Neden “ssh-t” üzerinden aktarılan bu ikili dosya değiştiriliyor?


29

Dosyaları SSH üzerinden kopyalamaya çalışıyorum , ancak scpihtiyacım olan dosya adını bilmediğim için kullanamıyorum . Küçük ikili dosyalar ve metin dosyaları iyi aktarılsa da, büyük ikili dosyalar değişebilir. İşte sunucudaki dosya:

remote$ ls -la
-rw-rw-r--  1 user user 244970907 Aug 24 11:11 foo.gz
remote$ md5sum foo.gz 
9b5a44dad9d129bab52cbc6d806e7fda foo.gz

İşte taşındıktan sonra dosya:

local$ time ssh me@server.com -t 'cat /path/to/foo.gz' > latest.gz

real    1m52.098s
user    0m2.608s
sys     0m4.370s
local$ md5sum latest.gz
76fae9d6a4711bad1560092b539d034b  latest.gz

local$ ls -la
-rw-rw-r--  1 dotancohen dotancohen 245849912 Aug 24 18:26 latest.gz

İndirilen dosyanın sunucudaki dosyadan daha büyük olduğuna dikkat edin ! Ancak, çok küçük bir dosyayla aynı şeyi yaparsam, her şey beklendiği gibi çalışır:

remote$ echo "Hello" | gzip -c > hello.txt.gz
remote$ md5sum hello.txt.gz
08bf5080733d46a47d339520176b9211  hello.txt.gz

local$ time ssh me@server.com -t 'cat /path/to/hello.txt.gz' > hi.txt.gz

gerçek 0m3.041s kullanıcı 0m0.013s sys 0m0.005s

local$ md5sum hi.txt.gz
08bf5080733d46a47d339520176b9211  hi.txt.gz

Her iki dosya boyutu da bu durumda 26 bayttır.

Neden küçük dosyalar iyi para transferleri yapabilir, ancak büyük dosyalar bunlara bazı baytlar ekler mi?


10
Bu var -ttransferini kırar seçeneği. Çok özel bir sebepten gerekmedikçe -tveya kullanmayın -T. Varsayılan durum çoğu durumda işe yarar, bu seçeneklere çok nadir ihtiyaç duyulur.
kasperd

3
Bunu bu yüzyılda söyleyeceğimi asla düşünmezdim, ancak ssh -t catdosya aktarmanın tek yolu uuencode ve uudecode'u denemek isteyebilirsiniz .
Mark Plotnick

1
@MarkPlotnick, uuencode / uudecode'un modern versiyonuna şimdi base64 / base64 -d adını
veriyor

Yanıtlar:


60

TL; DR

Kullanmayın -t. -tUzak ana bilgisayarda bir sözde terminal içerir ve yalnızca görsel uygulamaları bir terminalden çalıştırmak için kullanılmalıdır.

açıklama

Satır besleme karakteri (aynı zamanda newline olarak da bilinir \n) veya terminale gönderildiğinde terminale imlecini aşağı doğru hareket ettirmesini söyleyen karakterdir.

Yine de, seq 3bir terminalde koşarken , seqşöyle bir 1\n2\n3\nşeye yazdığı yer /dev/pts/0:

1
 2
  3

fakat

1
2
3

Neden?

Aslında seq 3(veya ssh host seq 3bu konuda) yazdığında 1\n2\n3\n, terminal görür 1\r\n2\r\n3\r\n. Yani, satır akışları satırbaşına (ki bu sırada terminaller imlecini ekranın soluna taşır) ve satır akışına çevrilmiştir.

Bu terminal aygıt sürücüsü tarafından yapılır. Daha doğrusu, terminalde (veya sözde terminal) cihazın hat disiplini, çekirdeğin içinde bulunan bir yazılım modülüdür.

Bu çizgi disiplininin davranışını sttykomutla kontrol edebilirsiniz . LF-> çevirisi ile CRLFaçıldı

stty onlcr

(genellikle varsayılan olarak etkindir). Şununla kapatabilirsiniz:

stty -onlcr

Veya tüm çıktı işlemlerini şu yöntemlerle kapatabilirsiniz:

stty -opost

Bunu yapar ve koşarsanız seq 3, göreceksiniz:

$ stty -onlcr; seq 3
1
 2
  3

beklenildiği gibi.

Şimdi, ne zaman:

seq 3 > some-file

seqartık bir terminale yazmıyor, bir dosyaya yazıyor, çeviri yapılmadı. Yani some-fileiçermiyor 1\n2\n3\n. Çeviri sadece bir terminal cihaza yazarken yapılır. Ve sadece gösterim için yapıldı.

Benzer şekilde, ne zaman:

ssh host seq 3

ssh1\n2\n3\nne sshçıktısı olursa olsun ne yazıyor .

Aslında olan, seq 3komutun hoststdout ile bir boruya yönlendirilmiş olarak çalıştırılmasıdır . Ana sshbilgisayardaki sunucu borunun diğer ucunu okur ve şifreli kanal üzerinden sshistemcinize gönderir ve sshmüşteri bu durumda stdout'una yazar, bu durumda, ekrana LFçevrildiği sahte terminal bir cihaz CRLF.

Birçok etkileşimli uygulama, stdout'ları bir terminal olmadığında farklı davranır. Örneğin, eğer çalıştırırsanız:

ssh host vi

vibeğenmedi, çıktısının bir boruya gitmesini sevmiyor. Örneğin imleç konumlandırma kaçış dizilerini anlayabilen bir cihazla konuşmadığını düşünüyor.

Yani sshbunun için bir -tseçenek var. Bu seçenekle, ana bilgisayardaki ssh sunucusu sahte terminal cihazı oluşturur ve bunun stdout (ve stdin ve stderr) olmasını sağlar vi. Ne vio, terminal cihazının yazdığı uzak uçbirimsi hat disiplin geçer ve tarafından okunur sshsunucu ve şifreli kanal üzerinden gönderilen sshistemci. Daha önce olduğu gibi aynı, bir boru kullanmak yerine , sshsunucunun sahte terminal kullanması dışında .

Diğer fark, müşteri tarafında, sshistemcinin terminali rawmoda ayarlamasıdır. Bu orada çeviri yapılmadığı anlamına gelir ( opostdevre dışı bırakılmış ve ayrıca giriş tarafı davranışları). Örneğin, yazarken Ctrl-C, kesmek yerine ssh, bu ^Ckarakter uzak tarafa gönderilir, burada uzak sözde terminalin çizgi disiplini, kesmeyi uzak komutuna gönderir .

Ne zaman yaparsın:

ssh -t host seq 3

seq 31\n2\n3\nBir sözde terminal aygıtı olan stdout'una yazar . Yüzünden onlcrtercüme olduğunu, ana bilgisayarda için 1\r\n2\r\n3\r\nve şifreli kanal üzerinden size gönderilen. Kendi tarafında çeviri ( onlcrdevre dışı) yoktur, bu yüzden 1\r\n2\r\n3\r\ndokunulmaz ( rawmod nedeniyle ) ve terminal emülatörünüzün ekranında doğru şekilde görüntülenir.

Şimdi, eğer yaparsan:

ssh -t host seq 3 > some-file

Yukarıdan fark yok. sshaynı şeyi yazacak:, 1\r\n2\r\n3\r\nama bu sefer içine some-file.

Yani temelde LF, çıktısının tümü girdi seqdiline CRLFçevrildi some-file.

Bunu yaparsanız aynı:

ssh -t host cat remote-file > local-file

Tüm LFkarakterler (0x0a bayt) CRLF'ye (0x0d 0x0a) çevrilmiştir.

Muhtemelen dosyanızdaki bozulmanın nedeni budur. İkinci daha küçük dosya durumunda, dosya 0x0a bayt içermez, bu nedenle bozulma olmaz.

Farklı tty ayarlarıyla farklı yolsuzluk türleri alabileceğinizi unutmayın. Başka bir olası yolsuzluk türü -t, eğer başlangıç ​​dosyalarınız host( ~/.bashrc, ~/.ssh/rc...) 'ın stderr'lerine bir şeyler yazıyorsa, çünkü -tuzak kabuğun stdout ve stderr'leri stdout ile birleştirilir ssh(ikisi de sözde olurlar) -terminal cihaz).

Uzaktan kumandanın catorada bir terminal cihazına çıkış yapmasını istemezsiniz .

İstediğiniz:

ssh host cat remote-file > local-file

Yapabilirsin:

ssh -t host 'stty -opost; cat remote-file` > local-file

İşe yarayacak ( yukarıda tartışılan stderr yolsuzluk vakası dışında ), ancak bu gereksiz sözde terminal-terminal katmanını çalıştıracağınız için bile alt-optimal olacaktır host.


Biraz daha eğlenceli:

$ ssh localhost echo | od -tx1
0000000 0a
0000001

TAMAM.

$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002

LF çevrildi CRLF

$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001

Tekrar tamam.

$ ssh -t localhost 'stty olcuc; echo x'
X

Bu, terminal hattı disiplini tarafından yapılabilecek bir çıktı son işleme biçimidir.

$ echo x | ssh -t localhost 'stty -opost; echo' | od -tx1
Pseudo-terminal will not be allocated because stdin is not a terminal.
stty: standard input: Inappropriate ioctl for device
0000000 0a
0000001

sshSunucuya, kendi girişi bir terminal olmadığında sahte terminal kullanmasını reddetti. -ttBununla zorlayabilirsin :

$ echo x | ssh -tt localhost 'stty -opost; echo' | od -tx1
0000000   x  \r  \n  \n
0000004

Çizgi disiplini giriş tarafında çok daha fazlasını yapar.

Burada, echogirişini okumuyor veya çıktısı istenmiyor x\r\n\n, peki bu nereden geliyor? Bu echo, uzak sözde terminalin ( stty echo) yerel adresidir . sshSunucu besleyen x\nuzak sözde-terminalinin ana yanına istemciden okuyun. Ve bunun çizgi disiplini onu geri yansıtıyor (daha önce stty opostçalıştırma, bu yüzden görüyoruz CRLFve görmüyoruz LF). Bu, uzak uygulamanın stdin'den bir şey okuyup okumamasından bağımsızdır.

$ (sleep 1; printf '\03') | ssh -tt localhost 'trap "echo ouch" INT; sleep 2'
^Couch

0x3Karakter geri yankılandı ^C( ^ve C) nedeniyle stty echoctlkabuk ve uyku nedeniyle SIGINT al stty isig.

Öyleyse:

ssh -t host cat remote-file > local-file

yeterince kötü ama

ssh -tt host 'cat > remote-file' < local-file

dosyaları diğer tarafa aktarmak için çok daha kötüdür. Tüm özel karakterleri ile> LF çeviri değil, aynı zamanda sorunları (- Bazı CR alırsınız ^C, ^Z, ^D, ^?, ^S...) ve ayrıca uzaktan catsonu ne zaman eof görmez local-fileulaşıldığında, sadece ^Dbir sonraki gönderilir \r, \nveya terminalinizde ^Dolduğu gibi başka cat > file.


5

Dosyayı kopyalamak için bu yöntemi kullanırken dosyalar farklı görünüyor.

Uzak sunucu

ls -l | grep vim_cfg
-rw-rw-r--.  1 slm slm 9783257 Aug  5 16:51 vim_cfg.tgz

Yerel sunucu

ssh ... catKomutunu çalıştırıyorum :

$ ssh dufresne -t 'cat ~/vim_cfg.tgz' > vim_cfg.tgz

Yerel sunucudaki bu dosyadaki sonuçlar:

$ ls -l | grep vim_cfg.tgz 
-rw-rw-r--. 1 saml saml 9820481 Aug 24 12:13 vim_cfg.tgz

Neden araştırılıyor?

Elde edilen dosyayı yerel tarafta araştırmak, bozuk olduğunu gösterir. -tDüğmeyi sshkomutunuzdan çıkarırsanız , beklendiği gibi çalışır.

$ ssh dufresne 'cat ~/vim_cfg.tgz' > vim_cfg.tgz

$ ls -l | grep vim_cfg.tgz
-rw-rw-r--. 1 saml saml 9783257 Aug 24 12:17 vim_cfg.tgz

Sağlama toplamları şimdi de çalışıyor:

# remote server
$ ssh dufresne "md5sum ~/vim_cfg.tgz"
9e70b036836dfdf2871e76b3636a72c6  /home/slm/vim_cfg.tgz

# local server
$ md5sum vim_cfg.tgz 
9e70b036836dfdf2871e76b3636a72c6  vim_cfg.tgz

Teşekkürler Sim. Aslında doğru cevabı veren ilk kişi sen olsan da, açıklamasının derinliği nedeniyle seçilen cevap için Stéphane'yi seçtim. Endişelenmeyin, öğrendiğim uzun bir gönderi geçmişiniz var ve tabii ki öğrendiğim bu yazıların altını çiziyorum. Teşekkür ederim.
dotancohen

@dotancohen - Endişelenmeyin, hangisinin A hissettiğinizi kabul ettiğinizi kabul edersiniz. Bunun neden olduğunu açıklayabilme yetenekleri, Gilles dışında, rakipsiz.
slm
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.