Truyền nhiều tệp qua stdin (qua ssh)


15

Tôi có một chương trình trên một máy chủ từ xa, mà tôi cần thực hiện tự động. Lệnh thực thi chương trình đó, trên cùng một máy, trông giống như thế này:

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

Trong trường hợp này, file1.txtfile2.txtđược sử dụng cho những thứ hoàn toàn khác nhau trong chương trình, vì vậy tôi không thể chỉ catchúng cùng nhau. Tuy nhiên, trong trường hợp của tôi, file1.txtfile2.txttôi muốn truyền vào chương trình chỉ tồn tại trên thiết bị của tôi chứ không phải trên máy chủ nơi tôi cần thực hiện chương trình. Tôi biết rằng tôi có thể cung cấp ít nhất một tệp thông qua SSH bằng cách chuyển qua stdin:

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

nhưng, vì tôi không được phép lưu trữ các tập tin trên máy chủ, nên tôi cũng cần một cách để file2.txtvượt qua nó. Tôi nghĩ có thể có thể thông qua việc lạm dụng các biến môi trường và sử dụng sáng tạo catsedcùng nhau, nhưng tôi không biết rõ các công cụ đủ để hiểu cách tôi sẽ sử dụng chúng để thực hiện điều này. Là nó có thể làm được, và làm thế nào?


2
catsedkhông phải là giải pháp ở đây.
Slyx

Có lẽ tôi có thể gắn kết, nhưng được cung cấp proxy và các ràng buộc bảo mật Tôi không chắc chắn tôi có thể thoát khỏi nó.
Áo choàng xanh Guy

Bạn có quyền trong máy từ xa để gắn thư mục ssh không?
Slyx

1
nếu bạn có thể mở phiên ssh từ máy từ xa sang máy cục bộ, thì không có vấn đề gì ở cấp mạng để gắn thư mục SSH.
Slyx

Bạn có thể làm chuyển tiếp? Những hệ thống và hệ vỏ nào bạn có ở đầu cục bộ và từ xa?
mosvy

Câu trả lời:


18

Nếu các tệp được cung cấp làm đối số cho chương trình của bạn là tệp văn bản và bạn có thể kiểm soát nội dung của chúng (bạn biết một dòng không xảy ra bên trong chúng), bạn có thể sử dụng nhiều tài liệu ở đây:

{
    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

Đây catlà một lệnh mẫu lấy tên tệp làm đối số. Nó có thể được thay thế:

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

Thay thế từng EOTcái bằng một cái gì đó không xảy ra trong mỗi tệp, tương ứng.


1
Cách tiếp cận này khá thông minh! Nếu các tệp không phải là văn bản, bạn có thể mã hóa / giải mã chúng bằng một cái gì đó như base64 hoặc thậm chí là ol 'uuencode tốt ... Rất hay!
filbranden

3
Lưu ý rằng một số (hầu hết) các triển khai sh triển khai tài liệu ở đây bằng các tệp tạm thời, do đó, nó có thể không hoạt động cho OP nếu chúng không được phép lưu trữ tệp trên máy chủ từ xa.
Stéphane Chazelas

2
dash(các /bin/shtrong debian, ubuntu, vv), busybox shthì /bin/shtừ FreeBSD và yashkhông sử dụng các file tạm ở đây-tài liệu. Tôi thực sự đã thử nó với một chroot chỉ đọc. Tất nhiên, các /dev/fd/tệp phải có sẵn - chỉ trên một hệ thống FreeBSD có /dev/fd/0-2sẵn, vì vậy fdescfsphải được gắn vào /dev/fd.
Hồi giáo

1
Tôi là một fan hâm mộ của bộ phân tách thông tin ASCII , được thiết kế để phân định các trường mà không bị cản trở. U+001Clà "Bộ phân tách thông tin bốn" (bộ tách tệp, FS) và lý tưởng cho trường hợp này, mặc dù các tệp nhị phân có thể sử dụng nó một cách trùng hợp. Do đó, tôi đề nghị EOT="$(printf $'\x1c')"trong các trình bao nâng cao hoặc cách khác EOT="$(awk 'BEGIN{printf"%c",28}')"để tương thích. Bạn sẽ cần trích dẫn nó khi bạn đặt nó vào vị trí, chẳng hạn như echo "$EOT$EOT$EOT"(tăng gấp ba lần để giảm tỷ lệ khớp với tệp nhị phân).
Adam Katz

Nếu bạn sẽ sử dụng tty tại sao không sử dụng ttylệnh? Trừ khi tôi thiếu một cái gì đó - mà tôi cho là có thể?
Pryftan

14

Có lẽ không chính xác những gì bạn muốn ... Nhưng có thể xem xét việc gửi một tarball qua đường ống được mở bởi ssh?

Bạn đã nói rằng:

Tôi không được phép lưu trữ tệp trên máy chủ.

Có thể bạn không có thư mục chính có thể ghi hoặc vị trí thuận tiện khác để lưu trữ tệp lâu dài, nhưng tôi nói không chắc là bạn không có bất kỳ vị trí có thể ghi nào, ngay cả khi tạm thời tmpfschỉ có sẵn cho bạn kết nối cụ thể.

Nhiều chương trình (và thậm chí các thói quen libc) yêu cầu có thể ghi được /tmp, vì vậy rất có thể một chương trình sẽ có sẵn cho bạn.

Sau đó, bạn có thể sử dụng một tập lệnh sẽ giải nén tarball vào một thư mục tạm thời, chạy chương trình của bạn và dọn dẹp thông qua kết nối ssh.

Cái gì đó như:

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

Điều này có thể cần một số chăm sóc thêm với đường dẫn tệp và có một số trường hợp góc để xem xét (kiểm tra cho chúng), nhưng cách tiếp cận chung sẽ hoạt động.

Nếu không có thư mục có thể ghi có sẵn, một cách tiếp cận có thể sẽ là sửa đổi programđể lấy tarball làm đầu vào đơn và giải nén nội dung của nó vào bộ nhớ. Chẳng hạn, nếu programlà một tập lệnh Python, thì việc sử dụng tarfilemô-đun tích hợp sẽ dễ dàng thực hiện một cái gì đó như thế.


3
Ngay cả khi không gặp phải rắc rối khi chơi tarball - tôi đã quyết định ít nhiều chỉ viết một tập tin /tmp/và sử dụng nó trực tiếp.
Áo choàng xanh Guy

2
Cách tiếp cận tarball có một số ưu điểm, khi bạn sử dụng một kết nối ssh duy nhất (không cần kiểm tra thành công / thất bại) và nhiều lần chạy đồng thời không có khả năng chạy vào nhau (sử dụng và / hoặc xóa các tệp trong / tmp có nghĩa là chạy riêng .) Nhưng tôi nghĩ đó là điều tốt nhất bạn sẽ nhận được từ ssh như nó tồn tại bây giờ. Chúc may mắn!
filbranden

3
@GreenCloakGuy Vì vậy, sau tất cả, bạn có thể lưu trữ các tệp trên máy chủ tốt. Hãy sửa đổi câu hỏi cho phù hợp.
Hồi giáo

1
@mosvy có thể , có, nhưng không muốn . Câu hỏi đặt ra là "có thể làm điều này mà không lưu trữ bất kỳ tập tin nào không", câu trả lời có vẻ là "có thể, nhưng không phải trong trường hợp của tôi"
Green Cloak Guy

5

Nếu bạn có thể đặt trình nghe TCP (có thể là cổng cao hơn), thì bạn có thể sử dụng phiên SSH thứ hai để thiết lập nguồn đầu vào thứ hai với nc.

Thí dụ:

Có đoạn script này trên máy chủ ( ~/script.bash):

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

Và có hai tệp này cục bộ:

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

Bây giờ đầu tiên bắt đầu nguồn thứ hai ( $servlà máy chủ):

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

Và chạy lệnh thích hợp:

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

2

Nó được thiết lập trong một bình luận/tmpthể ghi được, vì vậy chỉ cần sao chép qua một trong các tệp trước đó:

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

Điều này cũng dọn dẹp các tập tin sao chép sau một thành công (thay đổi &&một ;nếu bạn muốn loại bỏ nó bất kể thành công, nhưng sau đó lưu ý rằng bạn sẽ mất giá trị xuất cảnh).


Nếu điều đó không được chấp nhận, tôi sẽ đề xuất mày mò /path/to/programhoặc trình bao bọc cho nó có thể tách hai tệp khỏi một luồng đầu vào như:

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

Điều này sử dụng bốn phân tách thông tin ASCII (trình phân tách tệp, FS) và đã tăng gấp ba lần để chúng tôi giảm thiểu khả năng tệp nhị phân có chứa chuỗi đó. tweaked_programSau đó, bạn sẽ phân tách đầu vào được cung cấp dấu phân cách và sau đó hoạt động trên hai tệp đã lưu dưới dạng các biến.

Tất nhiên, nếu bạn sử dụng một ngôn ngữ có thư viện để xử lý tarball, một cách tiếp cận an toàn và sạch sẽ hơn chỉ đơn giản là chuyển tarsang mã như vậy sshnhư sau:

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

Và bạn tweaked_programsẽ giải nén và mở kho lưu trữ, lưu từng tệp vào một biến khác nhau và sau đó chạy programlogic gốc của các biến đó.


1

Nếu bạn được phép chuyển tiếp các cổng sshvà bạn có quyền truy cập wgetvào máy từ xa và busyboxtrên máy cục bộ, bạn có thể thực hiện một số việc như:

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

(sử dụng catnhư một chương trình ví dụ có hai đối số tệp).

Chỉ có khả năng chuyển tiếp cổng qua -Rlà cần thiết - thay vì thực hiện http, bạn có thể sử dụng các phương pháp khác, ví dụ: nếu bạn netcathỗ trợ -d-Ncác tùy chọ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)

Có thể có cách thay thế <(...)quy trình thay thế nếu vỏ đăng nhập trên máy từ xa không giống như bash.

Nói chung, điều này không quá tuyệt vời - kiến ​​thức tốt hơn về hệ thống / shell / config / quyền chính xác (mà bạn chưa cung cấp) có thể cho phép các giải pháp thông minh hơn.


-1

CẬP NHẬT: Điều này không thực sự hiệu quả, ssh có một ý tưởng rất rõ ràng về ý nghĩa của stdin và stdout / stderr và không thực sự cho phép sử dụng stderr để đọc từ nó. Tôi sẽ xóa câu trả lời này trong một vài ngày vì nó không hoạt động. Cảm ơn cuộc thảo luận rất thú vị !!!


Đáng tiếc là có không phải là một cách tuyệt vời để làm điều này trực tiếp, kể từ khi sshkhách hàng sẽ chỉ vượt qua ba file descriptor ( stdin, stdoutstderr ) đến máy chủ và nó không có quy định để vượt qua mô tả tập tin bổ sung (mà sẽ rất hữu ích đối với trường hợp sử dụng cụ này .)

(Cũng lưu ý rằng giao thức SSH có các điều khoản để vượt qua các mô tả tệp bổ sung, chỉ ssh khách hàng không thực hiện cách sử dụng tính năng đó. Về mặt lý thuyết, mở rộng ứng dụng khách bằng một bản vá sẽ đủ để hiển thị tính năng này.)

Một hacky cách để thực hiện những gì bạn đang tìm kiếm là sử dụng tập tin mô tả 2 ( stderrmô tả tập tin) để vượt qua các tập tin thứ hai. Cái gì đó như:

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

Điều này sẽ hoạt động, nó chỉ có thể gây ra vấn đề nếu programcố gắng viết vào stderr. Bạn có thể giải quyết vấn đề đó bằng cách tung lại các bộ mô tả tệp ở đầu từ xa trước khi bạn chạy chương trình. Bạn có thể di chuyển file2.txtđến tệp mô tả 3 và mở lại thiết bị xuất chuẩn để phản chiếu thiết bị xuất chuẩn:

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

Tôi chưa thực sự thử nghiệm điều này (gõ trên điện thoại) nhưng ít nhất về lý thuyết tôi mong nó hoạt động. Hãy cho tôi biết nếu vì lý do nào đó nó không. Chúc mừng!
filbranden

Điều đó sẽ không làm việc cả. Ngay cả với một ssh bị hack. AFAIK stderr không sử dụng một kênh riêng, nhưng một số loại tin nhắn đặc biệt. Nhưng có, việc có thể truy cập các kênh ssh dưới dạng ống hoặc ổ cắm tên miền unix chưa được đặt tên (thay vì phải chuyển tiếp ổ cắm) sẽ rất tuyệt, dường như không thể thực hiện được dưới bất kỳ hình thức hoặc hình dạng nào.
mosvy

@mosvy, fd 0, 1 và 2 trên lệnh từ xa sẽ là 3 đường ống khác nhau. Dữ liệu truyền qua 3 kênh khác nhau được ghép trong kết nối ssh, nhưng các kênh đó là một hướng (từ máy khách đến máy chủ cho fd 0 và từ máy chủ đến máy khách cho 1 và 2), do đó, ngay cả trên các hệ thống có đường ống hai chiều, sẽ thắng ' t làm việc Trên Linux, việc mở / dev / stderr ở chế độ chỉ đọc ở đầu kia của ống, vì vậy nó cũng không bao giờ hoạt động.
Stéphane Chazelas

Lưu ý rằng cái cuối cùng giả định shell đăng nhập của người dùng trên remote.name giống như Bourne ( 3<&2là toán tử shell Bourne và sshd chạy shell đăng nhập của người dùng để diễn giải lệnh do khách hàng gửi)
Stéphane Chazelas

2
@ StéphaneChazelas Không, một kênh ssh duy nhất được sử dụng cho stdin / stdout / stderr và dữ liệu stderr được truyền qua SSH_MSG_CHANNEL_EXTENDED_DATAtin nhắn với loại được đặt thành SSH_EXTENDED_DATA_STDERR. Bạn có thể đọc toàn bộ điều ở đây
mosvy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.