stdin을 통해 여러 파일 전달 (ssh를 통해)


15

원격 호스트에 프로그램이 있는데 그 실행을 자동화해야합니다. 동일한 컴퓨터에서 해당 프로그램을 실행하는 명령은 다음과 같습니다.

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

이 경우, file1.txt그리고 file2.txt프로그램 내에서 완전히 다른 일에 사용, 그래서 난 그냥 수 없습니다되어 cat그들을 함께. 그러나 필자의 경우 프로그램에 전달하려는 file1.txtfile2.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 2016 년

아마 나는 마운트 할 수 있었지만 프록시와 보안 제약이 주어지면 도망 칠 수 있을지 확신하지 못합니다.
녹색 망토 가이

원격 시스템에서 ssh 폴더를 마운트 할 권한이 있습니까?
Slyx 2016 년

1
원격 시스템에서 로컬 시스템으로 ssh 세션을 열 수 있다면 네트워크 수준에서 SSH 폴더를 마운트하는 데 아무런 문제가 없습니다.
Slyx

전달할 수 있습니까? 로컬 및 원격 시스템에 어떤 시스템과 쉘이 있습니까?
mosvy 2016 년

답변:


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 2016 년

3
여러 (대부분의) sh 구현은 여기에서 임시 파일을 사용하여 문서를 구현하므로 원격 호스트에 파일을 저장할 수 없으면 OP에서 작동하지 않을 수 있습니다.
Stéphane Chazelas 2016 년

2
dash합니다 ( /bin/sh데비안, 우분투 등의) busybox sh/bin/shFreeBSD의에서 그리고 yash여기-문서에 대한 임시 파일을 사용하지 않습니다. 실제로 읽기 전용 chroot로 테스트했습니다. 물론, /dev/fd/파일을 사용할 수 있습니다 - 주식 FreeBSD 시스템에만 /dev/fd/0-2, 그래서 fdescfs에 장착 할 수있다 /dev/fd.
mosvy 2016 년

1
나는 ASCII 정보 분리기 의 팬인데, 방해없이 필드를 구분하도록 설계되었습니다. U+001C"정보 구분 기호 4"(파일 구분 기호, FS)이며 이진 파일이 동시에 사용할 수 있지만이 경우에 이상적입니다. 따라서 EOT="$(printf $'\x1c')"고급 셸 또는 EOT="$(awk 'BEGIN{printf"%c",28}')"호환성을 제안 합니다. echo "$EOT$EOT$EOT"(이진 파일과의 일치 가능성을 낮추기 위해 삼진) 과 같이 제자리에 넣을 때 인용해야합니다 .
Adam Katz

tty를 사용하려면 왜 tty명령을 사용하지 않습니까? 내가 빠진 것이 아니라면 가능하다고 생각합니까?
Pryftan 2016 년

14

아마도 정확히 당신이 원하는 것은 아니지만 ... 아마도 ssh에 의해 열린 파이프를 통해 tarball을 보내는 것을 고려할 수 있습니까?

당신은 말했다 :

호스트에 파일을 저장할 수 없습니다.

쓰기 가능한 홈 디렉토리 나 파일을 장기간 저장할 수있는 다른 편리한 위치가 없을 수도 있지만 임시 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
  '

이것은 파일 경로에 대해 약간의주의가 필요할 수 있으며 고려해야 할 몇 가지 경우가 있지만 일반적인 접근 방식이 효과적입니다.

쓰기 가능한 디렉토리가 없으면 programtarball을 단일 입력으로 가져 와서 내용을 메모리에 압축 해제 하도록 수정하는 것이 가능 합니다. 예를 들어 programPython 스크립트 인 경우 내장 tarfile모듈 을 사용하면 쉽게 이와 같은 작업을 수행 할 수 있습니다.


3
tarballing의 어려움을 겪지 않아도 파일을 작성하고 /tmp/직접 사용 하기로 결정했습니다 .
Green Cloak Guy

2
tarball 접근 방식은 단일 ssh 연결을 사용하고 (성공 / 실패를 테스트 할 필요가 없음) 여러 개의 동시 실행이 서로 실행되지 않을 수 있으므로 (별도의 실행을위한 / tmp의 파일 사용 및 / 또는 삭제) .) 그러나 그것이 현재 존재하는 ssh에서 얻는 것이 최고라고 생각합니다. 행운을 빕니다!
filbranden 2016 년

3
@GreenCloakGuy 결국 서버에 파일을 저장할 있습니다. 그에 따라 질문을 수정하십시오.
mosvy 2016 년

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

이것은 또한 성공적인 실행합니다 (변경 후 복사 된 파일 정리 &&A와 ;관계없이 성공을 제거하려면,하지만 당신은 종료 값을 잃게됩니다주의를).


그것이 수용 할 수 없다면, /path/to/program다음과 같은 단일 입력 스트림에서 두 파일을 분리 할 수있는 땜장이 또는 래퍼를 제안 합니다.

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 정보 구분자 4 (파일 구분자, FS)를 사용하며이 문자열이 우연히 포함 된 이진 파일의 가능성을 최소화하기 위해 3 배가되었습니다. 귀하는 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 두 개의 파일 인수를 취하는 예제 프로그램으로 ).

-Rhttp를 수행하는 대신 포트를 전달하는 기능 만 필요합니다 . 예를 들어 다른 방법을 사용할 수 있습니다. 및 옵션을 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와 다른 경우 프로세스 대체 .

전체적으로 이것은 너무 크지 않습니다. 제공하지 않은 정확한 시스템 / 쉘 / 구성 / 권한에 대한 더 나은 지식은 더 똑똑한 솔루션을 제공 할 수 있습니다.


-1

업데이트 : 이것은 실제로 작동하지 않습니다 .ssh는 stdin 및 stdout / stderr의 의미에 대해 매우 정립되어 있으며 실제로 stderr을 읽을 수 없습니다. 작동하지 않으므로 며칠 안에이 답변을 삭제하겠습니다. 매우 흥미로운 토론에 감사드립니다 !!!


불행하게도 거기에 있기 때문에, 직접이 작업을 수행 할 수있는 좋은 방법이 아니다 ssh클라이언트 만 (세 개의 파일 디스크립터를 전달합니다는 stdin, stdoutstderr ) 서버에 그것은이 특정 사용 사례에 대한 도움이 될 것입니다 추가 파일 기술자를 (전달하는 조항이없는 .)

또한 SSH 프로토콜에는 추가 파일 디스크립터를 전달하는 조항이 있으며 ssh 클라이언트 해당 기능을 사용하는 방법을 구현하지 않습니다. 이론적으로 패치를 사용하여 클라이언트를 확장하면이 기능을 노출 할 수 있습니다.

찾고있는 것을 성취하는 해킹 방법 중 하나 는 파일 설명자 2 ( stderr파일 설명자)를 사용하여 두 번째 파일을 전달하는 것입니다. 다음과 같은 것 :

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

이것은 작동해야 program하며 stderr에 쓰려고 하면 문제가 발생할 수 있습니다 . 프로그램을 실행하기 전에 원격 끝에서 파일 디스크립터를 다시 저글링하여이 문제를 해결할 수 있습니다. file2.txt파일 디스크립터 3으로 이동 하고 stderr을 다시 열어 stdout을 미러링 할 수 있습니다 .

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

나는 이것을 실제로 테스트하지는 않았지만 (전화로 입력) 적어도 이론적으로는 그것이 작동 할 것으로 기대합니다. 어떤 이유로 든 그렇지 않은 경우 알려주십시오. 건배!
filbranden 2016 년

그것은 전혀 작동하지 않습니다. 해킹 된 ssh조차도 아닙니다. AFAIK stderr는 별도의 채널을 사용하지 않고 특별한 종류의 메시지를 사용합니다. 그러나 그렇습니다. 소켓 포워딩을 수행하는 대신 파이프 또는 명명되지 않은 유닉스 도메인 소켓으로 ssh 채널에 액세스 할 수 있다는 것은 어떤 종류 나 모양으로도 가능하지 않은 것 같습니다.
mosvy 2016 년

원격 명령의 @mosvy, fd 0, 1 및 2는 3 개의 다른 파이프입니다. 데이터는 ssh 연결에서 다중화 된 3 개의 서로 다른 채널을 통해 이동하지만 파이프는 양방향 시스템이므로 파이프가 양방향 인 시스템에서도 단일 방향 (fd 0의 경우 서버에서 클라이언트로, 서버에서 클라이언트 및 서버에서 1 및 2로) 작동하지 않습니다. Linux에서 / dev / stderr을 읽기 전용 모드로 열면 파이프의 다른 쪽 끝에 파이프가 제공되므로 작동하지 않습니다.
Stéphane Chazelas 2016 년

마지막 것은 remote.name에있는 사용자의 로그인 쉘이 Bourne과 같다고 가정합니다 ( 3<&2Bourne 쉘 연산자이며 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.