回答:
/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
グロブ...私は答えの中で言及したこれらのどれも。
lynxを使用します。
ほとんどのUnix / Linuxでかなり一般的です。
lynx -dump http://www.google.com
-dump:最初のファイルを標準出力にダンプして終了
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
はwgetに近い
クリス・スノーの回答から適応これはバイナリ転送ファイルも処理できます
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では、他のソリューションよりも優れている理由についてのメモをここに追加することができます。
「Bashだけで他に何も」を厳密に取り上げると、外部ユーティリティ(標準ユーティリティでさえも)を呼び出さず、バイナリファイルでも機能する、以前の回答(@Chris、@ 131)の1つの適応があります。
#!/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で終わるファイルでBash 4.4でテストされ、Debianのwget
およびcurl
バイナリもあります。373 kBのwget
バイナリのダウンロードには約5.7秒かかりました。約65 kB / sまたは512 kb / sを少し超える速度。
それに比べて、@ 131のcat-solutionは0.1秒未満で終了し、ほぼ100倍高速です。それほど驚くことではありません、本当に。
これは明らかに愚かです。外部ユーティリティを使用しないと、ダウンロードしたファイルでできることはほとんどなく、実行可能にすることすらできないからです。
echo
とprintf
(それが組み込み必要組み込み関数としてprintf
実装するがprintf -v
)
このパッケージをお持ちの場合libwww-perl
次を使用できます。
/usr/bin/GET
lynx
ソリューションよりも優れていると思います。
代わりに、ローカルマシンからSSH経由でアップロードを使用します
「最小のヘッドレス* nix」ボックスは、おそらくSSHで接続することを意味します。したがって、SSHを使用してアップロードすることもできます。もちろん、ダウンロードコマンドをヘッドレスサーバーのスクリプトに含める場合を除き、機能的には(ソフトウェアパッケージなどの)ダウンロードと同等です。
この回答に示されているように、ローカルマシンで次のコマンドを実行して、リモートヘッドレスサーバーにファイルを配置します。
wget -O - http://example.com/file.zip | ssh user@host 'cat >/path/to/file.zip'
3番目のマシンからのSSH経由の高速アップロード
ダウンロードと比較した上記のソリューションの欠点は、転送速度が遅いことです。ローカルマシンとの接続は、通常、ヘッドレスサーバーと他のサーバー間の接続よりも帯域幅がはるかに少ないためです。
それを解決するために、まともな帯域幅を持つ別のサーバーで上記のコマンドを実行できます。これをより快適にするために(3番目のマシンでの手動ログインを回避するため)、ローカルマシンで実行するコマンドを次に示します。
安全にするために、先頭のスペース文字を含む コマンドをコピーして貼り付け' '
ます。理由については、以下の説明を参照してください。
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' \
"
説明:
コマンドは3番目のマシンにsshをintermediate-host
実行し、そこからファイルのダウンロードwget
を開始し、target-host
SSH 経由でファイルのアップロードを開始します。ダウンロードとアップロードは帯域幅を使用intermediate-host
し、同時に発生します(Bashパイプに相当するため)ので、進行は速くなります。
これを使用する場合、2つのサーバーログイン(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
はbashビルトインであり、コードのこの部分がps
パスワード[ source ]を公開するため、出力に別のコマンドとして表示されないようにします。私が考えるのこの使用はことをsshpass
同じように安全なようであるsshpass -d<file-descriptor>
に推奨バリアントman sshpass
bashはこのように内部的にマッピングしているため、/dev/fd/*
とにかくファイルディスクリプタ。そして、一時ファイルを使用せずに[ ソース]。しかし、保証はありません。何かを見落としているかもしれません。
再び作るためにsshpass
使用状況の安全を、私たちはあなたのローカルマシン上のbash履歴に記録されてからコマンドを防ぐために必要です。そのため、コマンド全体の先頭にスペース文字が1つ追加されますが、この効果があります。
この-o StrictHostKeyChecking=no
部分は、ターゲットホストに接続したことがない場合にコマンドが失敗するのを防ぎます。(通常、SSHはユーザーの入力を待って接続試行を確認します。それでも続行します。)
sshpass
最後の引数としてssh
or 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