ผ่านหลายไฟล์ผ่าน stdin (ผ่าน ssh)


15

ฉันมีโปรแกรมบนรีโมตโฮสต์ซึ่งมีการดำเนินการที่ฉันต้องทำให้เป็นอัตโนมัติ คำสั่งรันโปรแกรมนั้นบนเครื่องเดียวกันจะมีลักษณะดังนี้:

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

ในกรณีนี้file1.txtและfile2.txtใช้สำหรับสิ่งต่าง ๆ ทั้งหมดภายในโปรแกรมดังนั้นฉันจึงไม่สามารถcatรวมเข้าด้วยกันได้ อย่างไรก็ตามในกรณีของฉันfile1.txtและfile2.txtฉันต้องการส่งผ่านเข้าไปในโปรแกรมนั้นมีอยู่ในอุปกรณ์ของฉันเท่านั้นไม่ใช่บนโฮสต์ที่ฉันต้องใช้งานโปรแกรม ฉันรู้ว่าฉันสามารถฟีดอย่างน้อยหนึ่งไฟล์ผ่าน SSH โดยผ่านมันstdin:

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

แต่เนื่องจากฉันไม่ได้รับอนุญาตให้จัดเก็บไฟล์บนโฮสต์ฉันจึงต้องหาวิธีที่จะไปที่file2.txtนั่นเช่นกัน ฉันคิดว่ามันอาจจะเป็นไปได้ผ่านการละเมิดของตัวแปรสภาพแวดล้อมและการใช้ความคิดสร้างสรรค์ของcatและsedกัน แต่ผมไม่ทราบว่าเครื่องมือที่ดีพอที่จะเข้าใจว่าฉันจะใช้พวกเขาเพื่อให้บรรลุนี้ เป็นไปได้หรือไม่และเป็นอย่างไร


2
catและsedไม่ใช่ทางออกที่นี่
Slyx

อาจเป็นไปได้ที่ฉันจะสามารถติดตั้ง แต่ได้รับพร็อกซีและข้อ จำกัด ด้านความปลอดภัยฉันไม่แน่ใจว่าฉันจะได้รับไปด้วย
เสื้อคลุมสีเขียว Guy

คุณมีสิทธิ์ในเครื่องระยะไกลเพื่อติดตั้งโฟลเดอร์ ssh หรือไม่?
Slyx

1
หากคุณสามารถเปิดเซสชัน ssh จากเครื่องรีโมตไปยังโลคัลของคุณดังนั้นจึงไม่มีปัญหาในระดับเครือข่ายเพื่อเมานต์โฟลเดอร์ SSH
Slyx

คุณสามารถส่งต่อได้หรือไม่ คุณมีระบบและเชลล์อะไรในโลคัลและรีโมตรีโมต?
mosvy

คำตอบ:


18

หากไฟล์ที่กำหนดเป็นอาร์กิวเมนต์ให้กับโปรแกรมของคุณเป็นไฟล์ข้อความและคุณสามารถควบคุมเนื้อหาของไฟล์เหล่านั้น (คุณรู้ว่าบรรทัดที่ไม่เกิดขึ้นภายในไฟล์นั้น) คุณสามารถใช้เอกสารที่นี่หลายรายการ:

{
    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

นี่catคือคำสั่งตัวอย่างซึ่งใช้ชื่อไฟล์เป็นอาร์กิวเมนต์ มันอาจจะแทน:

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

แทนที่แต่ละEOTสิ่งที่ไม่เกิดขึ้นในแต่ละไฟล์ตามลำดับ


1
วิธีนี้ค่อนข้างฉลาด! หากไฟล์ไม่ใช่ข้อความคุณสามารถเข้ารหัส / ถอดรหัสไฟล์ด้วยบางอย่างเช่น base64 หรือแม้แต่ ol 'uuencode ที่ดีมาก ... ดีมาก!
filbranden

3
โปรดทราบว่าการใช้งาน sh (ส่วนใหญ่) ใช้เอกสารที่นี่โดยใช้ไฟล์ชั่วคราวดังนั้นมันอาจไม่ทำงานสำหรับ OP หากไม่อนุญาตให้จัดเก็บไฟล์บนโฮสต์ระยะไกล
Stéphane Chazelas

2
dash(คน/bin/shในเดเบียน, อูบุนตู ฯลฯ ) busybox shที่/bin/shมาจาก FreeBSD และyashไม่ได้ใช้ไฟล์ชั่วคราวที่นี่เอกสาร ฉันทดสอบด้วย chroot แบบอ่านอย่างเดียวจริง ๆ แล้ว ของหลักสูตรที่/dev/fd/มีไฟล์จะพร้อมใช้งาน - บนระบบ FreeBSD หุ้นเท่านั้น/dev/fd/0-2ดังนั้นจะต้องมีการติดตั้งอยู่บนfdescfs /dev/fd
mosvy

1
ฉันเป็นแฟนตัวแยกข้อมูลของASCIIซึ่งออกแบบมาเพื่อคั่นเขตข้อมูลโดยไม่มีสิ่งกีดขวาง U+001Cคือ "Information Separator Four" (ตัวคั่นไฟล์, FS) และเหมาะสำหรับกรณีนี้แม้ว่าไฟล์ไบนารีอาจใช้งานโดยบังเอิญ ดังนั้นฉันขอแนะนำEOT="$(printf $'\x1c')"ในเชลล์ขั้นสูงหรืออื่น ๆEOT="$(awk 'BEGIN{printf"%c",28}')"เพื่อความเข้ากันได้ คุณจะต้องพูดมันเมื่อคุณวางไว้เช่นecho "$EOT$EOT$EOT"(สามเท่าเพื่อลดอัตราต่อรองของการจับคู่ไฟล์ไบนารี)
Adam Katz

หากคุณจะใช้ tty ทำไมไม่ใช้ttyคำสั่ง? ถ้าฉันไม่ได้ทำอะไร - ซึ่งเป็นไปได้?
Pryftan

14

อาจไม่ใช่สิ่งที่คุณต้องการ ... แต่อาจลองส่งทาร์บอลผ่านท่อที่เปิดโดย ssh

คุณพูดว่า:

ฉันไม่ได้รับอนุญาตให้จัดเก็บไฟล์บนโฮสต์

มันเป็นไปได้ว่าคุณไม่ได้มีไดเรกทอรีบ้านสามารถเขียนได้หรือสถานที่ที่สะดวกอื่น ๆ ที่จะจัดเก็บไฟล์ระยะยาว แต่ผมว่ามันไม่น่าที่คุณไม่ได้มีตำแหน่งที่เขียนใด ๆ แม้ว่าชั่วคราวtmpfsที่จะให้บริการเฉพาะสำหรับคุณ การเชื่อมต่อโดยเฉพาะ

โปรแกรมหลายโปรแกรม (และแม้กระทั่งรูทีน libc) จำเป็นต้องมีการเขียนได้/tmpดังนั้นจึงมีความเป็นไปได้มากที่คุณจะสามารถใช้งานได้

จากนั้นคุณสามารถใช้สคริปต์ที่จะแตก tarball ลงในไดเรกทอรีชั่วคราวเรียกใช้โปรแกรมของคุณและล้างข้อมูลผ่านการเชื่อมต่อ ssh

สิ่งที่ต้องการ:

$ 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
  '

สิ่งนี้อาจต้องใช้ความระมัดระวังเป็นพิเศษกับพา ธ ไฟล์และมีบางกรณีที่ต้องพิจารณา (ทดสอบสำหรับไฟล์) แต่วิธีการทั่วไปควรใช้งานได้

หากไม่มีไดเร็กทอรีที่สามารถเขียนได้วิธีที่เป็นไปได้คือการแก้ไขprogramเพื่อใช้ tarball เป็นอินพุตเดี่ยวและคลายเนื้อหาลงในหน่วยความจำ ตัวอย่างเช่นถ้าprogramเป็นสคริปต์ Python การใช้tarfileโมดูลในตัวจะทำให้สำเร็จได้อย่างง่ายดาย


3
แม้จะไม่ได้มีปัญหาในการ tarballing - ฉันตัดสินใจมากขึ้นหรือน้อยลงเพียงแค่เขียนไฟล์/tmp/และใช้งานโดยตรง
Green Cloak Guy

2
วิธี tarball มีข้อดีบางอย่างเนื่องจากคุณใช้การเชื่อมต่อ ssh เดียว (ไม่จำเป็นต้องทดสอบเพื่อความสำเร็จ / ล้มเหลว) และการทำงานพร้อมกันหลายรายการไม่น่าจะทำงานร่วมกัน (ใช้และ / หรือลบไฟล์ใน / tmp สำหรับการทำงานแยกกัน .) แต่ฉันคิดว่านั่นเป็นสิ่งที่ดีที่สุดที่คุณจะได้รับจาก ssh เหมือนเดิม โชคดี!
filbranden

3
@GreenCloakGuy ดังนั้นหลังจากทั้งหมดคุณสามารถจัดเก็บไฟล์บนเซิร์ฟเวอร์ได้ดี โปรดแก้ไขคำถามตาม
mosvy

1
@mosvy สามารถใช่ แต่ไม่ต้องการที่จะ คำถามนี้ตั้งอยู่ใน "มันเป็นไปได้ที่จะทำเช่นนี้โดยไม่ต้องจัดเก็บไฟล์ใด ๆ " คำตอบที่ดูเหมือนจะ "อาจ แต่ไม่ใช่ในกรณีของฉัน"
Green Cloak Guy

5

หากคุณสามารถตั้งค่าฟัง TCP (อาจจะเป็นพอร์ตที่สูงขึ้น) แล้วคุณสามารถใช้เซสชั่น SSH ncที่สองที่จะสร้างแหล่งสัญญาณที่สองกับ

ตัวอย่าง:

มีสคริปต์นี้บนเซิร์ฟเวอร์ ( ~/script.bash):

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

และมีไฟล์ทั้งสองนี้อยู่ภายในเครื่อง:

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

ตอนนี้ก่อนเริ่มต้นแหล่งที่สอง ( $servเป็นเซิร์ฟเวอร์):

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

และเรียกใช้คำสั่งที่เหมาะสม:

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

2

ก่อตั้งขึ้นในความคิดเห็นที่/tmpเขียนได้ดังนั้นเพียงคัดลอกไฟล์ใดไฟล์หนึ่งไว้ล่วงหน้า:

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

ซึ่งจะเป็นการล้างไฟล์ที่คัดลอกหลังจากการเรียกใช้สำเร็จ (เปลี่ยน&&เป็น;ถ้าคุณต้องการลบโดยไม่คำนึงถึงความสำเร็จ แต่โปรดทราบว่าคุณจะสูญเสียมูลค่าการออก)


หากไม่เป็นที่ยอมรับฉันจะเสนอ tinkering with /path/to/programหรือ wrapper ซึ่งสามารถแยกสองไฟล์ออกจากอินพุตสตรีมเดียวเช่น:

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

สิ่งนี้ใช้ตัวคั่นข้อมูล ASCII สี่ (ตัวคั่นไฟล์, FS) และมีสามเท่าเพื่อให้เราลดโอกาสของไฟล์ไบนารีที่มีสตริงนั้นโดยบังเอิญ คุณtweaked_programจะแบ่งอินพุตที่ได้รับจากตัวแยกและดำเนินการกับไฟล์ที่บันทึกสองไฟล์เป็นตัวแปร

แน่นอนว่าถ้าคุณใช้ภาษาที่มีห้องสมุดเพื่อจัดการกับ tarball วิธีที่ปลอดภัยกว่าและสะอาดกว่าก็คือการtarใส่รหัสเข้าไปในโค้ดsshเช่นนี้:

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

และคุณtweaked_programจะขยายและเปิดไฟล์เก็บถาวรบันทึกแต่ละไฟล์เป็นตัวแปรที่แตกต่างกันแล้วเรียกใช้programตรรกะดั้งเดิมของตัวแปรเหล่านั้น


1

หากคุณได้รับอนุญาตให้ส่งต่อพอร์ตผ่านsshและคุณสามารถเข้าถึงwgetบนเครื่องระยะไกลและไปยังbusyboxเครื่องท้องถิ่นคุณสามารถทำสิ่งต่าง ๆ เช่น:

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 $!

(ใช้catเป็นโปรแกรมตัวอย่างที่รับอาร์กิวเมนต์สองไฟล์)

ความสามารถในการส่งต่อพอร์ตผ่าน-Rเป็นสิ่งจำเป็นเท่านั้นแทนที่จะใช้ http คุณสามารถใช้วิธีอื่นเช่น หากการnetcatสนับสนุนของคุณ -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)

อาจมีวิธีการแทนที่การแทนที่<(...)กระบวนการหากเชลล์ล็อกอินบนเครื่องรีโมตไม่ใช่ ksh- หรือ bash- like

ทั้งหมดนี้ไม่ดีเกินไป - ความรู้ที่ดีขึ้นเกี่ยวกับระบบ / shells / config / การอนุญาตที่แน่นอน


-1

UPDATE:มันใช้งานไม่ได้จริง ๆ ssh มีความคิดที่ดีมากเกี่ยวกับความหมายของ stdin และ stdout / stderr และไม่อนุญาตให้ใช้ usurping stderr ในการอ่านจากมัน ฉันจะลบคำตอบนี้ในอีกไม่กี่วันเนื่องจากมันไม่ทำงาน ขอบคุณสำหรับการสนทนาที่น่าสนใจมาก !!!


น่าเสียดายที่ไม่มีวิธีที่ดีในการทำเช่นนี้โดยตรงเนื่องจากsshไคลเอนต์จะส่งไฟล์ descriptor สามไฟล์เท่านั้น (stdin , stdoutและstderr) ไปยังเซิร์ฟเวอร์และมันไม่ได้มีบทบัญญัติที่จะผ่านอธิบายไฟล์เพิ่มเติม (ซึ่งจะเป็นประโยชน์สำหรับกรณีการใช้งานนี้โดยเฉพาะ .)

(โปรดทราบว่าโปรโตคอล SSH มีข้อกำหนดให้ส่งผ่านตัวอธิบายไฟล์เพิ่มเติมเท่านั้น sshลูกค้าไม่ได้ใช้วิธีการใช้คุณลักษณะนั้นในทางทฤษฎีการขยายไคลเอ็นต์ด้วยแพตช์จะเพียงพอที่จะเปิดเผยคุณลักษณะนี้)

วิธีแฮ็กหนึ่งเพื่อให้ได้สิ่งที่คุณต้องการคือการใช้ file descriptor 2 ( stderrfile descriptor) เพื่อส่งไฟล์ที่สอง สิ่งที่ต้องการ:

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

สิ่งนี้ควรใช้งานได้เพียง แต่อาจทำให้เกิดปัญหาหากprogramพยายามเขียนถึง stderr คุณสามารถแก้ไขได้โดยการเล่นกลไฟล์ descriptors อีกครั้งที่ remote remote ก่อนที่คุณจะรันโปรแกรม คุณสามารถย้ายfile2.txtไปที่ file descriptor 3 และเปิด stderr ใหม่เพื่อ mirror stdout:

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

ฉันไม่ได้ทดสอบ (พิมพ์บนโทรศัพท์) จริง ๆ แต่อย่างน้อยในทางทฤษฎีฉันคาดหวังให้มันใช้งานได้ แจ้งให้เราทราบด้วยเหตุผลบางอย่างมันไม่ได้ ไชโย!
filbranden

นั่นไม่ได้ผลเลย ไม่แม้แต่กับ ssh ที่ถูกแฮ็ก AFAIK stderr ไม่ได้ใช้แชนเนลที่แยกต่างหาก แต่มีข้อความพิเศษบางอย่าง แต่ใช่ความสามารถในการเข้าถึงแชนเนล ssh เป็นไพพ์หรือซ็อกเก็ตโดเมน unix ที่ไม่มีชื่อ (แทนที่จะต้องทำการส่งต่อซ็อกเก็ต) จะดีมากมันดูเหมือนจะไม่เป็นไปได้ในรูปแบบหรือรูปแบบใด ๆ
mosvy

@mosvy, fd 0, 1 และ 2 บนคำสั่งระยะไกลจะเป็น 3 ท่อที่แตกต่างกัน ข้อมูลเดินทางผ่าน 3 ช่องสัญญาณที่แตกต่างกันมัลติเพล็กซ์ในการเชื่อมต่อ ssh แต่เป็นแบบทิศทางเดียว (จากไคลเอนต์ถึงเซิร์ฟเวอร์สำหรับ fd 0 และจากเซิร์ฟเวอร์หนึ่งไปยังไคลเอนต์ 1 และ 2) ดังนั้นแม้ในระบบที่ไพพ์เป็นแบบสองทิศทาง ไม่ทำงาน บน Linux openening / dev / stderr ในโหมด readonly ให้ที่ปลายอีกด้านหนึ่งของไปป์ดังนั้นมันจะไม่ทำงานเช่นกัน
Stéphane Chazelas

โปรดสังเกตว่าอันสุดท้ายถือว่าเชลล์ล็อกอินของผู้ใช้บน remote.name คือ Bourne-like ( 3<&2เป็นโอเปอเรเตอร์เชลล์ Bourne และ sshd รันเชลล์ล็อกอินของผู้ใช้เพื่อตีความคำสั่งที่ส่งโดยลูกค้า)
Stéphane Chazelas

2
@ StéphaneChazelas Nope, ช่อง SSH เดียวจะใช้สำหรับการ stdin / stdout / stderr และข้อมูล stderr จะถูกโอนผ่านทางข้อความที่มีชนิดชุดSSH_MSG_CHANNEL_EXTENDED_DATA SSH_EXTENDED_DATA_STDERRคุณสามารถอ่านสิ่งทั้งหมดได้ที่นี่
mosvy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.