ฉันมีหัวขาดน้อยที่สุด * ซึ่งไม่มียูทิลิตี้บรรทัดคำสั่งใด ๆ สำหรับการดาวน์โหลดไฟล์ (เช่นไม่มี curl, wget เป็นต้น) ฉันทุบตีเท่านั้น
ฉันจะดาวน์โหลดไฟล์ได้อย่างไร
เป็นการดีที่ฉันต้องการวิธีการแก้ปัญหาที่จะทำงานในหลากหลาย * ระวัง
ฉันมีหัวขาดน้อยที่สุด * ซึ่งไม่มียูทิลิตี้บรรทัดคำสั่งใด ๆ สำหรับการดาวน์โหลดไฟล์ (เช่นไม่มี curl, wget เป็นต้น) ฉันทุบตีเท่านั้น
ฉันจะดาวน์โหลดไฟล์ได้อย่างไร
เป็นการดีที่ฉันต้องการวิธีการแก้ปัญหาที่จะทำงานในหลากหลาย * ระวัง
คำตอบ:
หากคุณมี bash 2.04 หรือสูงกว่าเมื่อ/dev/tcp
เปิดใช้งานอุปกรณ์หลอกคุณสามารถดาวน์โหลดไฟล์จาก bash เองได้
วางรหัสต่อไปนี้ลงใน bash shell โดยตรง (คุณไม่จำเป็นต้องบันทึกรหัสลงในไฟล์เพื่อเรียกใช้งาน):
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
ประสงค์ trashes ทับขวาและช่องว่างชั้นนำ$line
จะ globwhile read
เช่นเดียวกับที่ทิ้งแบ็กสแลชและช่องว่างนำหน้าและ Bash ไม่สามารถจัดการกับ NUL ไบต์ได้เป็นอย่างดีดังนั้นไฟล์ไบนารีจะหมด และ$line
จะไม่พูดถึงเลย ... สิ่งนี้ไม่ได้เห็นในคำตอบ
ใช้คม
เป็นเรื่องปกติสำหรับ Unix / Linux ส่วนใหญ่
lynx -dump http://www.google.com
-dump: ดัมพ์ไฟล์แรกเพื่อ stdout และออก
man lynx
หรือ netcat:
/usr/bin/printf 'GET / \n' | nc www.google.com 80
หรือ telnet:
(echo 'GET /'; echo ""; sleep 1; ) | telnet www.google.com 80
lynx -source
ใกล้เข้ามาแล้ว
ดัดแปลงมาจากคำตอบของ 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 คุณอาจต้องการเพิ่มบันทึกเกี่ยวกับสาเหตุที่ทำงานได้ดีกว่าโซลูชันอื่น ๆ ที่นี่
รับ " เพียงแค่ทุบตีและไม่มีอะไรอื่น " อย่างเคร่งครัดนี่คือการปรับคำตอบก่อนหน้านี้ ( @ Chris's , @ 131's ) ที่ไม่เรียกใช้ยูทิลิตีภายนอกใด ๆ (ไม่ใช่แม้แต่มาตรฐานทั่วไป) แต่ยังทำงานกับไฟล์ไบนารี:
#!/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 และส่งกลับจริงถ้าพบหนึ่งเท็จถ้ามันไม่ได้ Bash ไม่สามารถจัดการ NUL ไบต์ในสตริงได้ดังนั้นเมื่อread
ส่งคืนด้วยจริงเราเพิ่ม NUL ไบต์ด้วยตนเองเมื่อพิมพ์และเมื่อมันคืนเท็จเรารู้ว่าไม่มี NUL ไบต์อีกต่อไปและควรเป็นข้อมูลชิ้นสุดท้าย .
ทดสอบกับ Bash 4.4 ในไฟล์ที่มี NUL อยู่ตรงกลางและลงท้ายด้วยศูนย์หนึ่งหรือสอง NUL และด้วยwget
และcurl
ไบนารีจาก Debian wget
ไบนารีขนาด 373 kB ใช้เวลาดาวน์โหลดประมาณ 5.7 วินาที ความเร็วประมาณ 65 kB / s หรือมากกว่า 512 kb / s
ในการเปรียบเทียบการแก้ปัญหาของแมวที่ 131 นั้นเสร็จในเวลาน้อยกว่า 0.1 วินาทีหรือเร็วกว่าเกือบร้อยเท่า ไม่น่าแปลกใจมากจริงๆ
เห็นได้ชัดว่ามันโง่เนื่องจากไม่มีการใช้ยูทิลิตี้ภายนอกมีหลายสิ่งที่เราสามารถทำได้กับไฟล์ที่ดาวน์โหลดมา
echo
และprintf
เป็น buildins (จำเป็นต้องมีprintf
printf -v
หากคุณมีแพ็คเกจนี้ libwww-perl
คุณสามารถใช้:
/usr/bin/GET
lynx
โซลูชันจริง ๆเพราะ Perl มีแนวโน้มที่จะติดตั้ง Lynx อย่างแน่นอน
ใช้การอัปโหลดแทนผ่าน SSH จากเครื่องท้องถิ่นของคุณ
กล่อง "หัวขาดน้อยที่สุด * ระวัง" หมายความว่าคุณอาจเป็น SSH ดังนั้นคุณสามารถใช้ SSH เพื่ออัปโหลดได้ ซึ่งเทียบเท่ากับการดาวน์โหลด (เช่นชุดซอฟต์แวร์ ฯลฯ ) ยกเว้นเมื่อคุณต้องการให้คำสั่งดาวน์โหลดรวมอยู่ในสคริปต์บนเซิร์ฟเวอร์ที่ไม่มีหัวของคุณ
ดังที่แสดงในคำตอบนี้คุณจะใช้งานสิ่งต่อไปนี้บนเครื่องโลคัลของคุณเพื่อวางไฟล์บนเซิร์ฟเวอร์ headless ระยะไกลของคุณ:
wget -O - http://example.com/file.zip | ssh user@host 'cat >/path/to/file.zip'
อัปโหลดได้เร็วขึ้นผ่าน SSH จากเครื่องที่สาม
ข้อเสียของวิธีการแก้ปัญหาข้างต้นเมื่อเปรียบเทียบกับการดาวน์โหลดนั้นเป็นความเร็วในการถ่ายโอนที่ต่ำกว่าเนื่องจากการเชื่อมต่อกับเครื่องของคุณมักจะมีแบนด์วิดท์น้อยกว่าการเชื่อมต่อระหว่างเซิร์ฟเวอร์ headless ของคุณและเซิร์ฟเวอร์อื่น ๆ
เพื่อแก้ปัญหานั้นคุณสามารถดำเนินการคำสั่งข้างต้นบนเซิร์ฟเวอร์อื่นที่มีแบนด์วิดท์ที่เหมาะสม ที่จะทำให้สะดวกสบายมากขึ้น (หลีกเลี่ยงการเข้าสู่ระบบคู่มือบนเครื่องที่สาม) ที่นี่เป็นคำสั่งในการดำเนินการในเครื่องท้องถิ่นของคุณ
เพื่อความปลอดภัยให้คัดลอกและวางคำสั่งนั้นรวมถึงอักขระช่องว่างนำ ' '
หน้า ดูคำอธิบายด้านล่างด้วยเหตุผล
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
เริ่มดาวน์โหลดไฟล์ไปที่นั่นผ่านทางwget
และเริ่มอัปโหลดไปยังtarget-host
ผ่าน SSH การดาวน์โหลดและการอัปโหลดใช้แบนด์วิดท์ของคุณintermediate-host
และเกิดขึ้นในเวลาเดียวกัน (เนื่องจากการเทียบเท่าไปป์ Bash) ดังนั้นความคืบหน้าจะรวดเร็ว
เมื่อใช้สิ่งนี้คุณต้องแทนที่การเข้าสู่ระบบเซิร์ฟเวอร์สองครั้ง ( user@*-host
) รหัสผ่านโฮสต์เป้าหมาย ( yourpassword
), URL ดาวน์โหลด ( http://example.com/…
) และเส้นทางผลลัพธ์บนโฮสต์เป้าหมายของคุณ ( /path/to/output-file.zip
) ด้วยค่าของตัวเองที่เหมาะสม
สำหรับ-T -e none
ตัวเลือก SSH เมื่อใช้มันในการถ่ายโอนไฟล์ดูคำอธิบายรายละเอียดเหล่านี้
คำสั่งนี้มีความหมายสำหรับกรณีที่คุณไม่สามารถใช้กลไกการตรวจสอบที่สำคัญ SSH ของสาธารณะ - ก็ยังคงเกิดขึ้นกับบางผู้ให้บริการโฮสติ้งที่ใช้ร่วมกันโดยเฉพาะอย่างยิ่งเป็นเจ้าภาพยุโรป เพื่อให้กระบวนการยังคงเป็นไปโดยอัตโนมัติเราพึ่งพาsshpass
เพื่อให้สามารถระบุรหัสผ่านในคำสั่ง มันจะต้องsshpass
มีการติดตั้งบนโฮสต์ระดับกลางของคุณ ( sudo apt-get install sshpass
ภายใต้ Ubuntu)
เราพยายามใช้sshpass
ในวิธีที่ปลอดภัย แต่ก็ยังไม่ปลอดภัยเท่ากับกลไก pubkey SSH (พูดman sshpass
) โดยเฉพาะอย่างยิ่งเราจัดหารหัสผ่าน SSH ไม่ได้เป็นอาร์กิวเมนต์บรรทัดคำสั่ง แต่ผ่านไฟล์ซึ่งถูกแทนที่ด้วยการทดแทนกระบวนการทุบตีเพื่อให้แน่ใจว่ามันไม่เคยมีอยู่บนดิสก์ printf
เป็นทุบตีในตัวการทำให้แน่ใจว่าเป็นส่วนหนึ่งของรหัสนี้ไม่ปรากฏขึ้นเป็นคำสั่งที่แยกต่างหากในps
การส่งออกเป็นที่จะเปิดเผยรหัสผ่าน [ แหล่ง ] ฉันคิดว่าการใช้งานนี้sshpass
มีความปลอดภัยเทียบเท่ากับsshpass -d<file-descriptor>
ตัวแปรที่แนะนำman sshpass
เนื่องจากทุบตีแมปภายในกับตัวให้/dev/fd/*
คำอธิบายไฟล์ และไม่ต้องใช้ไฟล์ temp [ แหล่งที่มา] แต่ไม่มีการรับประกันบางทีฉันอาจมองข้ามบางสิ่งบางอย่าง
อีกครั้งเพื่อให้การsshpass
ใช้งานปลอดภัยเราต้องป้องกันไม่ให้คำสั่งบันทึกลงในประวัติทุบตีบนเครื่องของคุณ สำหรับสิ่งนั้นคำสั่งทั้งหมดจะถูกเติมด้วยอักขระเว้นวรรคหนึ่งตัวซึ่งมีผลกระทบนี้
-o StrictHostKeyChecking=no
ส่วนป้องกันไม่ให้คำสั่งจากความล้มเหลวในกรณีที่มันไม่เคยเชื่อมต่อกับพื้นที่เป้าหมาย (โดยปกติแล้ว SSH จะรอให้ผู้ใช้ป้อนข้อมูลเพื่อยืนยันความพยายามในการเชื่อมต่อเราจะดำเนินการต่อไป)
sshpass
คาดว่า a ssh
หรือscp
คำสั่งเป็นอาร์กิวเมนต์สุดท้าย ดังนั้นเราจึงมีการเขียนโดยทั่วไปwget -O - … | ssh …
คำสั่งในรูปแบบโดยไม่ต้องท่อทุบตีเป็นตามที่อธิบายไว้ที่นี่
ตามสูตร @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