노력하고있어SSH를 통해 파일 복사scp
, 그러나 필요한 정확한 파일 이름을 모르기 때문에 작동하지 않습니다. 작은 바이너리 및 텍스트 파일은 잘 전송되지만 큰 바이너리 파일은 변경됩니다. 다음은 서버에 있는 파일입니다.
remote$ ls -la
-rw-rw-r-- 1 user user 244970907 Aug 24 11:11 foo.gz
remote$ md5sum foo.gz
9b5a44dad9d129bab52cbc6d806e7fda foo.gz
파일을 이동한 후의 파일은 다음과 같습니다.
local$ time ssh [email protected] -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
다운로드한 파일은 다음과 같으니 참고하세요.큰서버보다 낫네요! 그러나 매우 작은 파일로 동일한 작업을 수행하면 모든 것이 예상대로 작동합니다.
remote$ echo "Hello" | gzip -c > hello.txt.gz
remote$ md5sum hello.txt.gz
08bf5080733d46a47d339520176b9211 hello.txt.gz
local$ time ssh [email protected] -t 'cat /path/to/hello.txt.gz' > hi.txt.gz
실제 0m3.041s 사용자 0m0.013s 시스템 0m0.005s
local$ md5sum hi.txt.gz
08bf5080733d46a47d339520176b9211 hi.txt.gz
이 예에서 두 파일의 크기는 모두 26바이트입니다.
작은 파일은 잘 전송되지만 큰 파일은 일부 바이트를 추가하는 이유는 무엇입니까?
답변1
긴 이야기 짧게
을( 를) 사용하지 마십시오 -t
. -t
원격 호스트의 의사 터미널과 관련이 있으며 터미널에서 시각적 응용 프로그램을 실행하는 데에만 사용해야 합니다.
설명하다
개행 문자(newline 또는 newline 이라고도 함 \n
)는 터미널에 전송될 때 터미널에 커서를 아래쪽으로 이동하라고 지시하는 문자입니다.
seq 3
그러나 터미널에서 실행할 때, 즉 다음과 같이 seq
작성하면 다음이 표시되지 않습니다.1\n2\n3\n
/dev/pts/0
1
2
3
하지만
1
2
3
왜 그런 겁니까?
실제로 를 쓸 때 seq 3
(또는 ssh host seq 3
그 문제에 대해) 1\n2\n3\n
터미널은 을 봅니다 1\r\n2\r\n3\r\n
. 즉, 개행 문자는 캐리지 리턴 문자(이 시점에서 터미널이 커서를 화면 왼쪽으로 다시 이동함)와 개행 문자로 변환됩니다.
이는 터미널 장치 드라이버에 의해 수행됩니다. 보다 정확하게는 터미널(또는 의사 터미널) 장치의 연결 규칙에 따라 커널에 상주하는 소프트웨어 모듈입니다.
명령을 사용하여 이 선 규칙의 동작을 제어할 수 있습니다 stty
. LF
->번역 CRLF
통과
stty onlcr
(보통 기본적으로 활성화되어 있습니다). 다음 방법으로 끌 수 있습니다:
stty -onlcr
또는 다음을 사용하여 모든 출력 처리를 끌 수 있습니다.
stty -opost
이 작업을 수행하고 실행하면 seq 3
다음이 표시됩니다.
$ stty -onlcr; seq 3
1
2
3
예상대로.
이제 이렇게 하면:
seq 3 > some-file
seq
터미널 장치에 쓰는 대신 변환 없이 일반 파일에 씁니다. some-file
포용도 마찬가지다 1\n2\n3\n
. 변환은 터미널 장치에 쓸 때만 발생합니다. 그리고 이것은 단지 보여주기 위한 것입니다.
마찬가지로, 다음을 수행할 때:
ssh host seq 3
ssh
출력이 무엇이든 1\n2\n3\n
관계없이 작성됩니다 .ssh
실제로 일어나는 일은 seq 3
명령이 실행될 때 host
stdout이 파이프로 리디렉션된다는 것입니다. ssh
호스트의 서버는 파이프의 다른 쪽 끝을 읽고 이를 암호화된 채널을 통해 클라이언트로 보냅니다 ssh
. 클라이언트 ssh
는 이를 stdout(귀하의 경우 의사 터미널 장치)에 기록하여 LF
s가 CRLF
표시되도록 변환됩니다.
많은 대화형 응용 프로그램은 표준 출력이 터미널이 아닐 때 다르게 동작합니다. 예를 들어 다음을 실행하는 경우:
ssh host vi
vi
그것은 마음에 들지 않으며 출력이 파이프로 들어가는 것을 좋아하지 않습니다. 예를 들어 커서 위치 지정 이스케이프 시퀀스를 이해하는 장치와 통신하지 않는다고 생각합니다.
그래서 그 옵션이 ssh
있습니다 -t
. 이 옵션을 사용하면 호스트의 SSH 서버는 의사 터미널 장치를 생성하고 이를 의사 터미널 장치로 사용합니다 vi
. 터미널 장치에 기록된 콘텐츠는 vi
원격 의사 터미널 회선 규칙을 거쳐 서버에서 읽혀 지고 ssh
암호화된 채널을 통해ssh
관로, ssh
서버는의사 터미널.
또 다른 차이점은 클라이언트 측에서 ssh
클라이언트가 터미널을 raw
모드로 설정하고 로컬을 비활성화한다는 것입니다.에코). 이는 번역이 수행되지 않음을 의미합니다( opost
비활성화 및 기타 입력 측 동작). 예를 들어, 입력하면 해당 문자가 원격 끝으로 전송되고, Ctrl-C중단하는 대신 원격 의사 터미널의 줄 규칙이 실행됩니다.ssh
^C
방해하다원격 명령에.
이 작업을 수행할 때:
ssh -t host seq 3
seq 3
1\n2\n3\n
의사 터미널 장치인 표준 출력 에 씁니다 . onlcr
번역되었기 때문에호스트에서1\r\n2\r\n3\r\n
암호화된 채널을 통해 귀하에게 전송됩니다 . 귀하 측에 번역이 없으므로( onlcr
비활성화) 1\r\n2\r\n3\r\n
변경되지 않고(모드로 인해) 터미널 에뮬레이터 화면에 올바르게 표시됩니다.raw
이제 이렇게 하면:
ssh -t host seq 3 > some-file
위와 별 차이가 없습니다. ssh
같은 것을 쓸 것입니다: 1\r\n2\r\n3\r\n
, 그러나 이번에는 some-file
.
따라서 기본적 으로 모든 LF
출력 seq
은 .CRLF
some-file
이렇게 하면 결과는 동일합니다.
ssh -t host cat remote-file > local-file
모든 LF
문자(0x0a 바이트)는 CRLF(0x0d 0x0a)로 변환됩니다.
이것이 파일이 손상된 이유일 수 있습니다. 두 번째 작은 파일의 경우 파일에 0x0a 바이트가 포함되어 있지 않으므로 손상이 없습니다.
다른 tty 설정으로 인해 다른 유형의 손상이 발생할 수 있습니다. 이와 관련된 또 다른 잠재적 손상 유형은 -t
( , ...)의 시작 파일이 stderr에 무언가를 쓰는 경우입니다. 원격 셸의 stdout 및 stderr이 결국 stdout에 병합되기 때문입니다(둘 다 의사 터미널 장치로 이동함).host
~/.bashrc
~/.ssh/rc
-t
ssh
cat
원격 제어 출력이 최종 장치로 전달되는 것을 원하지 않습니다 .
당신이 원하는 것:
ssh host cat remote-file > local-file
다음을 수행할 수 있습니다.
ssh -t host 'stty -opost; cat remote-file' > local-file
그러면 작동할 것입니다(다음 경우는 제외).표준 오류에 쓰기위에서 논의한 피해), 그러나 이 역시 최적이 아닙니다 host
.
더 재미있는 것들이 있습니다:
$ ssh localhost echo | od -tx1
0000000 0a
0000001
좋아요
$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002
LF
번역하다CRLF
$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001
다시 한 번 알았습니다.
$ ssh -t localhost 'stty olcuc; echo x'
X
이는 터미널 라인 규칙을 통해 수행할 수 있는 출력 후처리의 또 다른 형태입니다.
$ 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
ssh
Reject는 서버 자체 입력이 터미널이 아닌 경우 의사 터미널을 사용하도록 서버에 지시합니다. -tt
하지만 강제로 적용할 수는 있습니다.
$ echo x | ssh -tt localhost 'stty -opost; echo' | od -tx1
0000000 x \r \n \n
0000004
라인 규율은 입력 측면에서 더 많은 작업을 수행합니다.
여기서는 echo
입력을 읽지 않고 출력하도록 요청하지도 않습니다. x\r\n\n
그렇다면 해당 입력은 어디서 오는 걸까요? 이는 echo
원격 의사 터미널( )의 로컬 터미널 입니다 stty echo
. 서버는 클라이언트에서 읽은 데이터를 원격 의사 터미널의 호스트로 ssh
전송합니다 . x\n
해당 행 규칙은 이를 반영합니다( before stty opost
가 run 이므로 CRLF
대신 a 가 표시됩니다 LF
). 원격 애플리케이션이 stdin에서 무엇이든 읽는지 여부는 중요하지 않습니다.
$ (sleep 1; printf '\03') | ssh -tt localhost 'trap "echo ouch" INT; sleep 2'
^Couch
왜냐하면 이 문자는 0x3
(and)로 에코되고 shell과 sleep은 SIGINT를 수신하기 때문입니다.^C
^
C
stty echoctl
stty isig
그래서 동시에:
ssh -t host cat remote-file > local-file
충분히 나쁘지만,
ssh -tt host 'cat > remote-file' < local-file
파일을 반대 방향으로 전송하는 것은 훨씬 더 나쁩니다. CR -> LF 변환이 이루어지지만 모든 특수 문자( ^C
, ^Z
, ^D
, ^?
, ^S
...) 에도 문제가 있으며 cat
eof 끝에 도달하면 원격 장치는 local-file
다음과 같은 경우 에만 eof를 볼 수 있습니다. ^D
터미널에서 수행하는 것과 유사한 작업 이후에 전송 합니다.\r
\n
^D
cat > file
답변2
이 방법을 사용하여 파일을 복사하면 파일이 다르게 보입니다.
원격 서버
ls -l | grep vim_cfg
-rw-rw-r--. 1 slm slm 9783257 Aug 5 16:51 vim_cfg.tgz
로컬 서버
다음 명령을 실행하세요 ssh ... cat
.
$ ssh dufresne -t 'cat ~/vim_cfg.tgz' > vim_cfg.tgz
로컬 서버에서 이 파일을 생성한 결과:
$ ls -l | grep vim_cfg.tgz
-rw-rw-r--. 1 saml saml 9820481 Aug 24 12:13 vim_cfg.tgz
원인을 조사해 볼까요?
생성된 파일을 로컬에서 조사한 결과 손상된 것으로 나타났습니다. 명령에서 -t
스위치를 꺼내면 예상대로 작동합니다.ssh
$ 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
이제 체크섬도 작동합니다.
# remote server
$ ssh dufresne "md5sum ~/vim_cfg.tgz"
9e70b036836dfdf2871e76b3636a72c6 /home/slm/vim_cfg.tgz
# local server
$ md5sum vim_cfg.tgz
9e70b036836dfdf2871e76b3636a72c6 vim_cfg.tgz