Birden çok dosyayı stdin'den geçirme (ssh üzeri)


15

Uzak bir ana bilgisayarda yürütülmesini otomatikleştirmem gereken bir programım var. Aynı makinede bu programı yürüten komut şuna benzer:

/path/to/program -a file1.txt -b file2.txt

Bu durumda file1.txtve file2.txtprogram içinde tamamen farklı şeyler için kullanılır, bu yüzden catonları birlikte yapamam . Ancak, benim durumumda, file1.txtve file2.txtben programa geçmek istiyorum bu değil ben programı çalıştırmak için gereken ana bilgisayarda cihazımda ürünlerinden ibarettir. SSH üzerinden geçirerek en az bir dosyayı besleyebileceğimi biliyorum stdin:

cat file1.txt | ssh host.name /path/to/program -a /dev/stdin -b file2.txt

ancak, ana bilgisayarda dosya depolamasına izin verilmediğim için, file2.txtoraya da ulaşmanın bir yoluna ihtiyacım var. Çevre değişkenlerinin kötüye kullanılması catve sedbirlikte ve yaratıcı bir şekilde kullanılması yoluyla mümkün olabileceğini düşünüyorum , ancak bunu başarmak için bunları nasıl kullanacağımı anlayacak kadar araç bilmiyorum. Bu yapılabilir mi ve nasıl?


2
catve sedburada çözüm değil.
Slyx

Muhtemelen bağlayabiliyordum, ancak proxy'ler ve güvenlik kısıtlamaları göz önüne alındığında ondan kurtulabileceğimden emin değilim.
Yeşil Pelerin Adam

Uzak makinede bir ssh klasörü bağlama izniniz var mı?
Slyx

1
uzak makineden yerel makinenize bir ssh oturumu açabiliyorsanız, ağ düzeyinde bir SSH klasörü bağlama sorunu yoktur.
Slyx

Yönlendirme yapabilir misiniz? Yerel ve uzak uçta hangi sistem ve kabuklarınız var?
mosvy

Yanıtlar:


18

Programınıza argüman olarak verilen dosyalar metin dosyalarıysa ve içeriklerini kontrol edebiliyorsanız (bunların içinde meydana gelmeyen bir satır biliyorsanız), burada birden çok belge kullanabilirsiniz:

{
    echo "cat /dev/fd/3 3<<'EOT' /dev/fd/4 4<<'EOT' /dev/fd/5 5<<'EOT'"
    cat file1
    echo EOT
    cat file2
    echo EOT
    cat file3
    echo EOT
} | ssh user@host sh

İşte catargümanlar olarak dosya adlarını alan bir örnek komuttur. Bunun yerine şunlar olabilir:

echo "/path/to/prog -a /dev/fd/3 3<<'EOT' -b /dev/fd/4 4<<'EOT'

EOTHer birini sırasıyla dosyaların her birinde meydana gelmeyen bir şeyle değiştirin .


1
Bu yaklaşım oldukça zekidir! Dosyalar metin değilse, base64 veya hatta iyi ol 'uuencode gibi bir şeyle kodlayabilir / deşifre edebilirsiniz ... Çok güzel!
filbranden

3
Birkaç (en) sh uygulamasının burada geçici dosyalar kullanarak belge uyguladığına dikkat edin, bu nedenle uzak ana bilgisayarda dosya depolamasına izin verilmiyorsa OP için çalışmayabilir.
Stéphane Chazelas

2
dash( /bin/shdebian, ubuntu, vb), busybox sh, /bin/shFreeBSD gelen ve yashburada-belgeler için geçici dosyaları kullanmayın. Aslında salt okunur bir chroot ile test ettim. Tabii ki, /dev/fd/dosyalar mevcut olmalıdır - sadece bir stok FreeBSD sistemi /dev/fd/0-2üzerinde, bu yüzden fdescfsmonte edilmelidir /dev/fd.
mosvy

1
Ben engel olmadan alanları sınırlamak için tasarlanmış ASCII bilgi ayırıcılar hayranıyım . U+001C"Bilgi Ayırıcı Dört" (dosya ayırıcı, FS) ve ikili dosyalar tesadüfen yararlanabilir, ancak bu durum için idealdir. Bu nedenle, EOT="$(printf $'\x1c')"gelişmiş kabuklarda veya EOT="$(awk 'BEGIN{printf"%c",28}')"uyumluluk için başka bir şey öneririm . Yerine koyduğunuzda alıntı yapmanız gerekir, örneğin echo "$EOT$EOT$EOT"(bir ikili dosyayı eşleştirme olasılığını azaltmak için üçe katlanır).
Adam Katz

Eğer tty kullanacaksanız neden ttykomutu kullanmıyorsunuz ? Bir şeyi kaçırmadıkça - sanırım hangisi mümkün?
Pryftan

14

Belki tam olarak ne istediğinizi değil ... Ama belki ssh tarafından açılan borudan bir tarball göndermeyi düşünebilirsiniz?

Dedin ki:

Ana bilgisayarda dosya depolamama izin verilmiyor.

Dosyaları uzun süreli saklamak için yazılabilir bir giriş dizininiz veya başka bir uygun konumunuz olmayabilir, tmpfsancak yalnızca sizin için kullanılabilir hale getirilecek bir geçici olsa bile yazılabilir bir konumunuzun bulunma olasılığının düşük olduğunu söyleyebilirim . belirli bağlantı.

Birçok program (ve hatta libc rutinleri) yazılabilir bir yazı gerektirir /tmp, bu yüzden büyük olasılıkla bir tane kullanılabilir.

Daha sonra tarball'ı geçici bir dizine açacak, programınızı çalıştıracak ve ssh bağlantısı üzerinden temizleyecek bir komut dosyası kullanabilirsiniz.

Gibi bir şey:

$ tar cf - file1.txt file2.txt |
  ssh host.name '
      set -e
      tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX)
      cleanup () { rm -rf "$tmpdir"; }
      trap cleanup EXIT
      cd "$tmpdir"
      tar xf -
      /path/to/program -a file1.txt -b file2.txt
  '

Bu, dosya yollarıyla biraz daha dikkat gerektirebilir ve dikkate alınması gereken bazı köşe vakaları vardır (bunlar için test edin), ancak genel yaklaşım işe yaramalıdır.

Yazılabilir bir dizin yoksa, programbir tarball'ı tek giriş olarak almak ve içeriğini belleğe açmak için olası bir yaklaşım olacaktır . Örneğin program, bir Python betiğiyse, yerleşik tarfilemodülü kullanmak böyle bir şeyi kolayca başarabilir.


3
Tarballing zahmetinden bile geçmeden - az çok bir dosya yazmaya /tmp/ve doğrudan kullanmaya karar verdim .
Yeşil Pelerin Adamı

2
Tarball yaklaşımının bazı avantajları vardır, çünkü tek bir ssh bağlantısı kullandığınızda (başarı / başarısızlığı test etmeye gerek yoktur) ve birden fazla eşzamanlı çalışmanın birbirine girmesi muhtemel değildir (/ tmp içindeki dosyaları ayrı çalıştırmalar için kullanın ve / veya silin Ama sanırım şu anda olduğu gibi ssh'den alacağınız en iyisi bu. İyi şanslar!
filbranden

3
@GreenCloakGuy Yani sonuçta sen yapabilirsiniz gayet sunucuda dosya saklamak. Lütfen soruyu buna göre değiştirin.
mosvy

1
@mosvy olabilir , evet, ama istemiyorum . Soru, "herhangi bir dosya kaydetmeden bunu yapmak mümkün mü" şeklinde duruyor, cevabı "belki, ama benim durumumda değil" gibi görünüyor
Green Cloak Guy

5

Bir TCP dinleyicisi ayarlayabilirseniz (daha yüksek bir bağlantı noktası olabilir), ikinci giriş kaynağını oluşturmak için ikinci bir SSH oturumu kullanabilirsiniz nc.

Misal:

Sunucuda şu komut dosyası var ( ~/script.bash):

#!/usr/bin/env bash
cat "$1" | tr a 1
nc localhost "$2" | tr '[:lower:]' '[:upper:]'

Ve yerel olarak şu iki dosya var:

$ cat a 
aaa
aaa
aaa
$ cat b
bbb
bbb
bbb

Şimdi önce ikinci kaynağı başlatın ( $servsunucudur):

ssh "$serv" nc -l -p 2222 -q1 <b &

Ve komutu doğru şekilde çalıştırın:

$ ssh "$serv" ./script.bash - 2222 <a
111
111
111
BBB
BBB
BBB
[1]+  Done                    ssh "$serv" nc -l -p 2222 -q1 < b

2

Bu bir kuruldu yorumun/tmp yüzden sadece önceden dosyalardan birinin üzerine kopyalayın yazılabilir:

scp -p file2.txt host.name:/tmp/
ssh host.name "/path/to/program -a /dev/stdin -b /tmp/file2.txt && rm /tmp/file2.txt" < file1.txt

Bu, başarılı bir çalıştırmadan sonra kopyalanan dosyayı da temizler (başarıdan bağımsız olarak kaldırmak istiyorsanız &&bir olarak değiştirin ;, ancak çıkış değerini kaybedeceğinizi unutmayın).


Kabul edilebilir değilse /path/to/program, iki dosyayı aşağıdaki gibi tek bir giriş akışından ayırabilen bir sarmalayıcı veya bunun için bir sarmalayıcı öneriyorum :

awk 'FNR == 1 && NR > 1 { printf "%c%c%c", 28, 28, 28 } 1' file1.txt file2.txt \
  | ssh host.name /path/to/tweaked_program

Bu ASCII bilgi ayırıcısı dört (dosya ayırıcı, FS) kullanır ve üçe katlar, böylece bu dizeyi rastlantısal olarak içeren bir ikili dosyanın şansını en aza indiririz. Daha tweaked_programsonra ayırıcı verilen girdiyi böler ve kaydedilen iki dosya üzerinde değişken olarak çalışırsınız.

Tabii ki, tarball'larla başa çıkmak için kütüphaneleri olan bir dil kullanıyorsanız, daha güvenli ve daha temiz bir yaklaşım, tarböyle bir koda bağlanmak olacaktır ssh:

tar -zpc file1.txt file2.txt |ssh host.name /path/to/tweaked_program

Ve tweaked_programarşivi açar ve açar, her dosyayı farklı bir değişkene kaydeder ve daha sonra orijinalin programmantığını değişkenler üzerinde çalıştırırsınız .


1

Bağlantı noktalarını iletme sshizniniz varsa wgetve uzak makinede ve busyboxyerel makinede erişiminiz varsa, şöyle bir şey yapabilirsiniz:

mkdir /tmp/test; cd /tmp/test
echo 1st_file > 1st_file
echo 2nd_file > 2nd_file

busybox httpd -f -p 127.0.0.1:11080 &
ssh USER@HOST -R 10080:127.0.0.1:11080 '
        cat <(wget -q -O- http://localhost:10080/1st_file) \
            <(wget -q -O- http://localhost:10080/2nd_file)
'
kill $!

( catiki dosya argümanı alan örnek bir program olarak kullanmak ).

Yalnızca bağlantı noktalarını yönlendirme yeteneği -Rönemlidir - http yerine başka yöntemler kullanabilirsiniz, örn. eğer ve seçenekleri netcatdestekliyorsa :-d-N

nc -Nl localhost 11001 < 1st_file &
nc -Nl localhost 11002 < 2nd_file &
ssh USER@HOST -R 10001:localhost:11001 -R 10002:localhost:11002 '
        cat <(nc -d localhost 10001) <(nc -d localhost 10002)

<(...)Uzak makinedeki oturum açma kabuğu ksh veya bash benzeri değilse , işlem değişikliklerini değiştirmenin yolları olabilir .

Sonuçta, bu çok büyük değil - kesin sistem / kabuklar / yapılandırma / izinler (daha önce sağladığınız) hakkında daha iyi bilgi, daha akıllı çözümlere izin verebilir.


-1

GÜNCELLEME: Bu gerçekten işe yaramaz, ssh, stdin ve stdout / stderr'in ne anlama geldiğine dair çok kesin bir fikre sahiptir ve stderr'ın ondan okumasına gerçekten izin vermez. Bu cevabı çalışmadığı için birkaç gün içinde sileceğim. Çok ilginç tartışma için teşekkürler !!!


Maalesef orada beri, doğrudan yapmak için harika bir yol değil sshistemci sadece üç dosya tanımlayıcıları geçecek ( stdin, stdoutve stderr) sunucuya ve bu özel kullanım durumu için yararlı olacaktır ek dosya tanımlayıcıları (geçmesine karşılıkları bulunmamaktadır .)

(Ayrıca SSH protokolünün ek dosya tanımlayıcılarını iletmek için hükümleri olduğunu unutmayın, yalnızca sshistemci bu özelliği kullanmanın bir yolunu uygulamaz. Teorik olarak, istemciyi bir yama ile genişletmek bu özelliği ortaya çıkarmak için yeterli olacaktır.)

Bir hacky Eğer aradığınızı Çözüme ulaşmanın yolu dosya tanıtıcı 2 (kullanmaktır stderrikinci dosyayı geçmek için Dosya tanıtıcı). Gibi bir şey:

$ ssh remote.name \
      /path/to/program -a /dev/stdin -b /dev/stderr \
      <file1.txt 2<file2.txt

Bu çalışmalıdır, programstderr'a yazmaya çalışırsa bir soruna neden olabilir . Programı çalıştırmadan önce uzak uçtaki dosya tanımlayıcılarını yeniden hokkabazlayarak bu sorunu çözebilirsiniz. file2.txtDosya tanımlayıcı 3'e geçebilir ve stdout'u yansıtmak için stderr'ı yeniden açabilirsiniz:

$ ssh remote.name \
      /path/to/program -a /dev/stdin -b /dev/fd/3 \
      '3<&2' '2>&1' \
      <file1.txt 2<file2.txt

Bunu gerçekten test etmedim (telefonda yazarak) ama en azından teoride çalışmasını beklerdim. Herhangi bir sebepten ötürü değilse bana bildirin. Şerefe!
filbranden

Bu hiç işe yaramayacak. Saldırıya uğramış bir ssh ile bile değil. AFAIK stderr ayrı bir kanal kullanmaz, ancak özel bir mesaj türü kullanır. Ancak evet, ssh kanallarına borular veya adsız unix etki alanı yuvaları (soket iletimi yapmak yerine) olarak erişebilmek harika olurdu, herhangi bir şekilde veya şekilde mümkün görünmüyor.
mosvy

@mosvy, fd 0, 1 ve 2 uzaktan kumandadaki 3 farklı boru olacaktır. Veriler ssh bağlantısında çoğullanan 3 farklı kanaldan geçer, ancak bunlar tek yönlüdür (fd 0 için istemciden şiddete ve 1 ve 2 için sunucudan istemciye), bu nedenle boruların çift yönlü olduğu sistemlerde bile, Çalışmıyor. Linux'ta, / dev / stderr'i salt okunur modda açmak, borunun diğer ucunu verir, bu yüzden de işe yaramaz.
Stéphane Chazelas

Sonuncunun remote.name üzerindeki kullanıcının oturum açma kabuğunun Bourne benzeri olduğunu varsayacağını unutmayın ( 3<&2Bourne kabuk operatörüdür ve sshd, istemci tarafından gönderilen komutu yorumlamak için kullanıcının oturum açma kabuğunu çalıştırır)
Stéphane Chazelas

2
@ StéphaneChazelas Hayır, stdin / stdout / stderr için tek bir ssh kanalı kullanılır ve stderr verileri, SSH_MSG_CHANNEL_EXTENDED_DATAtürüne ayarlanmış mesajlarla aktarılır SSH_EXTENDED_DATA_STDERR. Her şeyi burada
mosvy
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.