bash脚本中的算术运算问题|算术运算符无效

bash脚本中的算术运算问题|算术运算符无效

从 bash 脚本中,我使用 call 从服务器下载文件curl。现在我想检查文件是否已完全下载。为此,我比较了下载文件和标头的大小Content-Length。尽管两者相等,但我收到以下错误:

")syntax error: invalid arithmetic operator (error token is "

bash 脚本的一部分:

remote_size=$(curl -kI "${HEADERS[@]}" "$url" | grep -i content-length | awk '{print $2}')
                local_size=$(stat --format=%s "$dest/$file")
                if (( remote_size == local_size )); then
                        echo "File is complete" >&2
                elif (( remote_size > local_size )); then
                        echo "Download is incomplete" >&2
                elif (( remote_size < local_size )); then
                        echo "Remote file shrunk -- probably should delete local and start over" >&2
                fi

灵感来自:https://stackoverflow.com/questions/37885503/check-if-curl-incremental-continue-at-download-is-successful/37885681?

谁能告诉我这里有什么问题以及如何解决它?

提前致谢

PS:我正在 ubuntu(WSL 上)进行测试,但该脚本最终将成为嵌入式平台上 Linux 的一部分。如果缺少任何信息,请告诉我

答案1

$ curl -sI https://google.com | sed -n '/content-length/l'
content-length: 220\r$

请参阅行尾的回车符(又名CR, \r, )(是表示行尾的方式)。 HTTP 标头^M$sed用 CRLF 分隔,而 Unix 行分隔符是 LF。

在 bash 和其他类似 Korn 的 shell 中的算术表达式中使用未经清理的数据是一个命令注入漏洞,这里更存在一个问题,即您使用了-kaka--insecure选项,允许 MitM 攻击者在响应中注入任意标头。

在 GNU 系统上,您可以使用:

local_size=$(stat -Lc %s -- "$dest/$file") || die
remote_size=$(curl -sI -- "$url" | LC_ALL=C grep -Piom1 '^content-length:\s*\K\d+') ||
  die "No content-length"
case $((local_size - remote_size)) in
  (0) echo same;;
  (-*) echo remote bigger;;
  (*) echo local bigger;;
esac

通过仅返回\d+C 语言环境中的匹配内容,我们确保remote_size仅包含十进制 ASCII 数字,从而消除了 ACE 漏洞。

该 GNU 命令的标准等效项grep可能是:

LC_ALL=C sed '/^[Cc][Oo][Nn][Tt][Ee][Nn][Tt]-[Ll][Ee][Nn][Gg][Tt][Hh]:[[:space:]]*\([0-9]\{1,\}\).*/!d;s//\1/;q'

需要注意的是,如果找不到标头,它不会返回错误的退出状态就像grep这样做,因此您需要额外检查[ -n "$remote_size" ].

die上面可以是:

die() {
  [ "$#" -eq 0 ] || printf>&2 '%s\n' "$@"
  exit 1
}

(适应您想要使用的任何日志记录机制)。

另请注意,虽然这在实践中不太可能发生,但标题可以折叠。例如,内容长度标头可以返回为:

Content-Length:<CR>
 123456<CR>

提取标头值的一种方法是使用formail专门用于处理 RFC822 标头的工具:

remote_size=$(curl... | formail -zcx content-length -U content-length)

对于-U content-length,如果有多个Content-Length 标头,则返回最后一个标头。更改-U-u返回第一个,如grep -m1上面所示。

您仍然需要清理结果或使用['s(不是[[...]]'s!)-lt/ -eq/-gt运算符,而不是((...))避免 ACE 漏洞。

使用curl7.84.0 或更高版本,您还可以curl直接使用以下命令提供该标头的值:

remote_size=$(curl -w '%header{content-length}' -sIo /dev/null -- "$url") || die

通过测试我发现

  • 如果标头出现多次,则仅返回第一个标头的值
  • 如果该值不是以数字开头(可选地以 a 开头),它会抱怨+,但仍然需要对其后传递的任何字符进行清理。
  • 它确实支持折叠标题,但拒绝第一行具有空值的内容长度。

相关内容