如何仅使用 bash 而不使用其他任何东西(不使用curl、wget、perl 等)来下载文件

如何仅使用 bash 而不使用其他任何东西(不使用curl、wget、perl 等)来下载文件

我有一个最小的无头 *nix不具有任何用于下载文件的命令行实用程序(例如,没有 curl、wget 等)。我只有 bash。

我怎样才能下载文件?

理想情况下,我想要一个能够在广泛的 *nix 上工作的解决方案。

答案1

如果您有 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>&-
}

然后您可以从 shell 执行它,如下所示:

__wget http://example.iana.org/

来源:莫雷基的回答通过 cygwin 命令行升级和安装包?

更新: 正如评论中提到的,上面概述的方法很简单:

  • read意志会丢弃反斜杠和前导空格。
  • Bash 不能很好地处理 NUL 字节,因此二进制文件被淘汰。
  • 不加引号$line将导致全局。

答案2

使用山猫。

对于大多数 Unix/Linux 来说这很常见。

lynx -dump http://www.google.com

-dump:将第一个文件转储到标准输出并退出

man lynx

或者网猫:

/usr/bin/printf 'GET / \n' | nc www.google.com 80

或远程登录:

(echo 'GET /'; echo ""; sleep 1; ) | telnet www.google.com 80

答案3

改编自克里斯·斯诺的回答。这也可以处理二进制文件。

function __curl() {
  read -r proto server path <<<"$(printf '%s' "${1//// }")"
  if [ "$proto" != "http:" ]; then
    printf >&2 "sorry, %s supports only http\n" "${FUNCNAME[0]}"
    return 1
  fi
  DOC=/${path// //}
  HOST=${server//:*}
  PORT=${server//*:}
  [ "${HOST}" = "${PORT}" ] && PORT=80

  exec 3<>"/dev/tcp/${HOST}/$PORT"
  printf 'GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' "${DOC}" "${HOST}" >&3
  (while read -r line; do
   [ "$line" = $'\r' ] && break
  done && cat) <&3
  exec 3>&-
}
  • 我 break && cat 要出去read
  • 我使用 HTTP 1.0,因此无需等待/发送连接:关闭。

您可以像这样测试二进制文件:

$ __curl http://www.google.com/favicon.ico >   mine.ico
$ curl   http://www.google.com/favicon.ico > theirs.ico
$ md5sum mine.ico theirs.ico
f3418a443e7d841097c714d69ec4bcb8  mine.ico
f3418a443e7d841097c714d69ec4bcb8  theirs.ico

答案4

使用本地计算机上的 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 下载文件到那里,并开始通过 SSHwget上传到那里。target-host下载和上传使用您的带宽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以安全的方式使用,但它仍然不会像 SSH 公钥机制那样安全(说man sshpass)。特别是,我们不是以命令行参数的形式提供 SSH 密码,而是通过文件提供,该文件由 bash 进程替换来替换,以确保它永远不会存在于磁盘上。这printf是一个内置的 bash,确保这部分代码不会在ps输出中作为单独的命令弹出,因为这会暴露密码 [来源]。我思考这种使用与 中推荐的变体sshpass一样安全,因为 bash无论如何都会在内部将其映射到这样的文件描述符。并且不使用临时文件[sshpass -d<file-descriptor>man sshpass/dev/fd/*来源]。但不能保证,也许我忽略了一些事情。

  • 同样,为了确保sshpass使用安全,我们需要防止该命令被记录到本地计算机上的 bash 历史记录中。为此,整个命令前面都会添加一个空格字符,从而达到此效果。

  • -o StrictHostKeyChecking=no部分可防止命令在从未连接到目标主机的情况下失败。 (通常情况下,SSH 会等待用户输入来确认连接尝试。无论如何,我们都会让它继续进行,以免中间主机上出现无限期挂起的命令。)

  • sshpass期望sshorscp命令作为其最后一个参数。所以我们必须重写典型的wget -O - … | ssh …命令重写为没有 bash 管道的形式,如所解释的这里

相关内容