파일을 다운로드하기위한 명령 줄 유틸리티 (예 : curl, wget 등) 가 없는 최소 헤드리스 * nix 가 있습니다 . 나는 배쉬 만 가지고있다.
파일을 어떻게 다운로드합니까?
이상적으로는 광범위한 * nix에서 작동하는 솔루션을 원합니다.
파일을 다운로드하기위한 명령 줄 유틸리티 (예 : curl, wget 등) 가 없는 최소 헤드리스 * nix 가 있습니다 . 나는 배쉬 만 가지고있다.
파일을 어떻게 다운로드합니까?
이상적으로는 광범위한 * nix에서 작동하는 솔루션을 원합니다.
답변:
/dev/tcp
의사 장치를 사용 하여 bash 2.04 이상을 사용하는 경우 bash 자체에서 파일을 다운로드 할 수 있습니다.
다음 코드를 bash 쉘에 직접 붙여 넣으십시오 (실행하기 위해 파일에 코드를 저장할 필요는 없습니다).
function __wget() {
: ${DEBUG:=0}
local URL=$1
local tag="Connection: close"
local mark=0
if [ -z "${URL}" ]; then
printf "Usage: %s \"URL\" [e.g.: %s http://www.google.com/]" \
"${FUNCNAME[0]}" "${FUNCNAME[0]}"
return 1;
fi
read proto server path <<<$(echo ${URL//// })
DOC=/${path// //}
HOST=${server//:*}
PORT=${server//*:}
[[ x"${HOST}" == x"${PORT}" ]] && PORT=80
[[ $DEBUG -eq 1 ]] && echo "HOST=$HOST"
[[ $DEBUG -eq 1 ]] && echo "PORT=$PORT"
[[ $DEBUG -eq 1 ]] && echo "DOC =$DOC"
exec 3<>/dev/tcp/${HOST}/$PORT
echo -en "GET ${DOC} HTTP/1.1\r\nHost: ${HOST}\r\n${tag}\r\n\r\n" >&3
while read line; do
[[ $mark -eq 1 ]] && echo $line
if [[ "${line}" =~ "${tag}" ]]; then
mark=1
fi
done <&3
exec 3>&-
}
그런 다음 셸에서 다음과 같이 실행할 수 있습니다.
__wget http://example.iana.org/
출처 : Moreaki 의 대답 은 cygwin 명령 줄을 통해 패키지를 업그레이드하고 설치합니까?
업데이트 : 의견에서 언급했듯이 위에서 설명한 접근 방식은 간단합니다.
read
의지는 백 슬래시 선도 공백을 삭제 한 사용자.$line
가 붙지 않습니다.while read
쓰레기와 같은 백 슬래시와 선행 공백 및 Bash는 NUL 바이트를 아주 잘 처리 할 수 없으므로 바이너리 파일이 없습니다. 그리고 따옴표 $line
가 붙지 않을 것입니다.
살 lyn이를 사용하십시오.
대부분의 유닉스 / 리눅스에서 일반적입니다.
lynx -dump http://www.google.com
-dump : 첫 번째 파일을 stdout에 덤프하고 종료
man lynx
또는 netcat :
/usr/bin/printf 'GET / \n' | nc www.google.com 80
또는 텔넷 :
(echo 'GET /'; echo ""; sleep 1; ) | telnet www.google.com 80
lynx -source
는 wget에 더 가깝습니다
Chris Snow 답변에서 적응 됨 이진 전송 파일도 처리 할 수 있습니다.
function __curl() {
read proto server path <<<$(echo ${1//// })
DOC=/${path// //}
HOST=${server//:*}
PORT=${server//*:}
[[ x"${HOST}" == x"${PORT}" ]] && PORT=80
exec 3<>/dev/tcp/${HOST}/$PORT
echo -en "GET ${DOC} HTTP/1.0\r\nHost: ${HOST}\r\n\r\n" >&3
(while read line; do
[[ "$line" == $'\r' ]] && break
done && cat) <&3
exec 3>&-
}
이진 파일을 다음과 같이 테스트 할 수 있습니다
ivs@acsfrlt-j8shv32:/mnt/r $ __curl http://www.google.com/favicon.ico > mine.ico
ivs@acsfrlt-j8shv32:/mnt/r $ curl http://www.google.com/favicon.ico > theirs.ico
ivs@acsfrlt-j8shv32:/mnt/r $ md5sum mine.ico theirs.ico
f3418a443e7d841097c714d69ec4bcb8 mine.ico
f3418a443e7d841097c714d69ec4bcb8 theirs.ico
cat
. 실제 파일 데이터를로 읽으므로 작동해야합니다 . 그것이 부정 행위인지 ( 순전히 쉘 이 아니기 때문에 ) 또는 좋은 해결책 ( cat
결국 표준 도구 이기 때문에) 인지 확실하지 않습니다 . 그러나 @ 131에서는 다른 솔루션보다 왜 더 나은지에 대한 메모를 추가 할 수 있습니다.
은 "촬영 단지 강타하고 아무것도 다른 사람을 "엄격하게, 여기에 (이전 답변 중 하나 적응의 크리스의 @ , 131 개의 @ 외부 유틸리티 (심지어 표준 것들)뿐만 아니라 바이너리 파일과 함께 작동을 호출하지 않습니다)
#!/bin/bash
download() {
read proto server path <<< "${1//"/"/ }"
DOC=/${path// //}
HOST=${server//:*}
PORT=${server//*:}
[[ x"${HOST}" == x"${PORT}" ]] && PORT=80
exec 3<>/dev/tcp/${HOST}/$PORT
# send request
echo -en "GET ${DOC} HTTP/1.0\r\nHost: ${HOST}\r\n\r\n" >&3
# read the header, it ends in a empty line (just CRLF)
while IFS= read -r line ; do
[[ "$line" == $'\r' ]] && break
done <&3
# read the data
nul='\0'
while IFS= read -d '' -r x || { nul=""; [ -n "$x" ]; }; do
printf "%s$nul" "$x"
done <&3
exec 3>&-
}
와 함께 사용하십시오 download http://path/to/file > file
.
우리는 NUL 바이트를 처리 read -d ''
합니다. NUL 바이트까지 읽은 후 발견되면 true를, 그렇지 않으면 false를 리턴합니다. Bash는 문자열에서 NUL 바이트를 처리 할 수 없으므로 read
true로 반환하면 인쇄 할 때 NUL 바이트를 수동으로 추가하고 false를 반환하면 더 이상 NUL 바이트가 없다는 것을 알고 있으며 이것이 마지막 데이터 여야 함 .
중간에 NUL이 있고 0, 1 또는 2 개의 NUL로 끝나는 파일과 데비안의 바이너리 wget
및 curl
바이너리 로 Bash 4.4로 테스트했습니다 . 373kB wget
바이너리를 다운로드하는 데 약 5.7 초가 걸렸습니다. 약 65 kB / s의 속도 또는 512 kb / s 이상의 비트.
이에 비해 @ 131의 cat-solution은 0.1 초 미만 또는 거의 백배 더 빠르게 완료됩니다. 정말 놀라운 일이 아닙니다.
외부 유틸리티를 사용하지 않으면 다운로드 한 파일로 수행 할 수있는 작업이 많지 않고 실행 파일로 만들 수 없기 때문에 이는 명백히 어리석은 일입니다.
echo
와 printf
내장 명령으로 (그것은 내장 필요 printf
구현을 printf -v
)
로컬 컴퓨터에서 SSH를 통해 대신 업로드 사용
"최소 헤드리스 * nix"박스는 아마도 SSH로 연결되어 있음을 의미합니다. 따라서 SSH를 사용하여 업로드 할 수도 있습니다 . 이것은 물론 헤드리스 서버의 스크립트에 다운로드 명령을 포함시키려는 경우를 제외하고 는 (소프트웨어 패키지 등의) 다운로드와 기능적으로 동일합니다 .
이 답변에 표시된 것처럼 로컬 헤드 시스템 에서 다음을 실행 하여 원격 헤드리스 서버에 파일을 배치합니다.
wget -O - http://example.com/file.zip | ssh user@host 'cat >/path/to/file.zip'
세 번째 컴퓨터에서 SSH를 통한 빠른 업로드
로컬 시스템과의 연결은 일반적으로 헤드리스 서버와 다른 서버 간의 연결보다 대역폭이 훨씬 적기 때문에 다운로드와 비교하여 위의 솔루션의 단점은 전송 속도가 느리다는 것입니다.
이를 해결하기 위해 적절한 대역폭을 가진 다른 서버에서 위의 명령을 실행할 수 있습니다. 세 번째 컴퓨터에서 수동 로그인을 피하는 것이 더 편안하도록 로컬 컴퓨터 에서 실행하는 명령이 있습니다 .
보안을 유지하려면 선행 공백 문자를 포함하여 해당 명령 을 복사 하여 붙여 넣으십시오 ' '
. 이유는 아래 설명을 참조하십시오.
ssh user@intermediate-host "sshpass -f <(printf '%s\n' yourpassword) \
ssh -T -e none \
-o StrictHostKeyChecking=no \
< <(wget -O - http://example.com/input-file.zip) \
user@target-host \
'cat >/path/to/output-file.zip' \
"
설명 :
이 명령은 세 번째 컴퓨터로 ssh하고을 intermediate-host
통해 파일을 다운로드 한 다음 SSH wget
를 target-host
통해 파일을 업로드하기 시작 합니다. 다운로드 및 업로드는 대역폭을 사용하며 intermediate-host
동시에 Bash 파이프와 동등한 기능으로 인해 진행되므로 진행 속도가 빠릅니다.
이를 사용할 때는 두 개의 서버 로그인 ( user@*-host
), 대상 호스트 비밀번호 ( yourpassword
), 다운로드 URL ( http://example.com/…
) 및 대상 호스트 ( /path/to/output-file.zip
) 의 출력 경로를 적절한 자체 값 으로 바꿔야 합니다.
를 들어 -T -e none
이 파일을 전송하는 데 사용하는 SSH 옵션, 볼 이 자세한 설명을 .
이 명령은 SSH의 공개 키 인증 메커니즘을 사용할 수없는 경우를위한 것으로, 일부 공유 호스팅 공급자, 특히 Host Europe에서 여전히 발생합니다 . 프로세스를 계속 자동화하기 sshpass
위해 명령에 비밀번호를 제공 할 수 있어야합니다. sshpass
중간 호스트 ( sudo apt-get install sshpass
Ubuntu 아래) 에 설치 해야 합니다 .
우리 sshpass
는 안전한 방식 으로 사용하려고 하지만 SSH pubkey 메커니즘만큼 안전하지는 않습니다 (예 :) man sshpass
. 특히 SSH 암호를 명령 줄 인수가 아니라 파일을 통해 제공합니다.이 암호는 디스크에 존재하지 않도록 bash 프로세스 대체로 대체됩니다. 는 printf
확인 코드의이 부분에 별도의 명령으로 팝업하지 않습니다 만드는 내장 떠들썩한이며, ps
그 암호 [노출 될 수로 출력 소스 ]. 나는 생각 이 사용하는 것이 sshpass
단지와 같이 안전 sshpass -d<file-descriptor>
에 추천 변형 man sshpass
bash는 이러한 내부적으로 매핑하기 때문에, /dev/fd/*
어쨌든 파일 기술자. 그리고 임시 파일을 사용하지 않고 [그 소스]. 그러나 보장 할 수는 없습니다. 어쩌면 내가 간과했을 수도 있습니다.
다시 sshpass
안전하게 사용하려면 로컬 컴퓨터의 bash 기록에 명령이 기록되지 않도록해야합니다. 이를 위해 전체 명령 앞에 하나의 공백 문자가 붙어있어이 효과가 있습니다.
-o StrictHostKeyChecking=no
부분은 대상 호스트에 연결되지 않을 경우에는 실패하는 명령을 방지한다. (일반적으로 SSH는 연결 시도를 확인하기 위해 사용자 입력을 기다립니다. 어쨌든 계속 진행합니다.)
sshpass
마지막 인수로 ssh
또는 scp
명령을 기대합니다 . 따라서 여기wget -O - … | ssh …
에서 설명하는 것처럼 일반적인 명령을 bash 파이프가없는 형태로 다시 작성해야합니다 .
@Chris Snow 레시피를 기반으로합니다. 나는 약간의 개선을했다 :
코드는 다음과 같습니다.
function __wget() {
: ${DEBUG:=0}
local URL=$1
local tag="Connection: close"
if [ -z "${URL}" ]; then
printf "Usage: %s \"URL\" [e.g.: %s http://www.google.com/]" \
"${FUNCNAME[0]}" "${FUNCNAME[0]}"
return 1;
fi
read proto server path <<<$(echo ${URL//// })
local SCHEME=${proto//:*}
local PATH=/${path// //}
local HOST=${server//:*}
local PORT=${server//*:}
if [[ "$SCHEME" != "http" ]]; then
printf "sorry, %s only support http\n" "${FUNCNAME[0]}"
return 1
fi
[[ x"${HOST}" == x"${PORT}" ]] && PORT=80
[[ $DEBUG -eq 1 ]] && echo "SCHEME=$SCHEME" >&2
[[ $DEBUG -eq 1 ]] && echo "HOST=$HOST" >&2
[[ $DEBUG -eq 1 ]] && echo "PORT=$PORT" >&2
[[ $DEBUG -eq 1 ]] && echo "PATH=$PATH" >&2
exec 3<>/dev/tcp/${HOST}/$PORT
if [ $? -ne 0 ]; then
return $?
fi
echo -en "GET ${PATH} HTTP/1.1\r\nHost: ${HOST}\r\n${tag}\r\n\r\n" >&3
if [ $? -ne 0 ]; then
return $?
fi
# 0: at begin, before reading http response
# 1: reading header
# 2: reading body
local state=0
local num=0
local code=0
while read line; do
num=$(($num + 1))
# check http code
if [ $state -eq 0 ]; then
if [ $num -eq 1 ]; then
if [[ $line =~ ^HTTP/1\.[01][[:space:]]([0-9]{3}).*$ ]]; then
code="${BASH_REMATCH[1]}"
if [[ "$code" != "200" ]]; then
printf "failed to wget '%s', code is not 200 (%s)\n" "$URL" "$code"
exec 3>&-
return 1
fi
state=1
else
printf "invalid http response from '%s'" "$URL"
exec 3>&-
return 1
fi
fi
elif [ $state -eq 1 ]; then
if [[ "$line" == $'\r' ]]; then
# found "\r\n"
state=2
fi
elif [ $state -eq 2 ]; then
# redirect body to stdout
# TODO: any way to pipe data directly to stdout?
echo "$line"
fi
done <&3
exec 3>&-
}
echo -en "GET ${PATH} HTTP/1.1\r\nHost: ${HOST}\r\n${tag}\r\n\r\n" >&3
, ${tag}
지정되지 않았습니다.
tag
변수가 올바른 세트 로이 답변을 편집하면 이제 잘 작동합니다.
gawk