【问题标题】:While cURL is downloading execute stopwatch script在 cURL 下载时执行秒表脚本
【发布时间】:2018-04-03 03:35:00
【问题描述】:

我有一些类似的秒表脚本:

BEGIN=$(date +%s)

while true; do
   NOW=$(date +%s)
   DIFF=$(($NOW - $BEGIN))
   MINS=$(($DIFF / 60))
   SECS=$(($DIFF % 60))
   HOURS=$(($DIFF / 3600))

   # \r  is a "carriage return" - returns cursor to start of line
   printf "\rDownload time: %02d:%02d:%02d" $HOURS $MINS $SECS
   sleep 1
done

所以while 某些条件为真,它会在每个循环中持续增加 1 秒。我希望这种情况大致如下:

function download()
{
    HOMEPAGE_RESPONSE=$(curl -w "\n%{http_code}" "https://example.com/")

    STATUS_CODE=$(echo "$HOMEPAGE_RESPONSE" | sed -n '$p')

    HTML=$(echo "$HOMEPAGE_RESPONSE" | sed '$d')
}

download

# Whenever the STATUS_CODE is 200, exit the stopwatch script
# Can be any other condition that stops the loop when cURL has finished
while (( $STATUS_CODE != 200 )); do
   NOW=$(date +%s)
   DIFF=$(($NOW - $BEGIN))
   MINS=$(($DIFF / 60))
   SECS=$(($DIFF % 60))
   HOURS=$(($DIFF / 3600))

   # \r  is a "carriage return" - returns cursor to start of line
   printf "\rDownload time: %02d:%02d:%02d" $HOURS $MINS $SECS
   sleep 1
done

这个想法是启动 cURL 下载,在下载开始的同时,执行秒表脚本。这最终将开始计算秒数并打印秒表until 下载完成。我也知道我在这个post 中找到的until 命令。示例:

until $(curl --output /dev/null --silent --head --fail http://myhost:myport); do
    printf '.'
    sleep 5
done

我不知道如何应用 until,因为我的 cURL 存储在名为 download() 的函数内的一个变量中,我希望能够分别使用 STATUS_CODEHTML 内容。

谁能告诉我该怎么做?


更新

鉴于@Inian 的回答,这就是我目前所拥有的:

function download()
{
    homepage_response=$(curl -s -w "\n%{http_code}" "https://example.com/")
    status_code=$(echo "$homepage_response" | sed -n '$p')
    html=$(echo "$homepage_response" | sed '$d')
    printf '%s' "${status_code}"
}

# Calling the function $(download)
until [[ "$(download)" == "200" ]]; do
    printf '.\n'
    sleep 1
done

echo $status_code

根据我的理解,这应该执行函数download()并在不同的行上打印.每个until cURL返回status_code200

然而,这会启动 cURL,但它既不打印 .,也不回显应该等同于 200status_code

我猜不出为什么。


改变的答案

根据@chepner 的回答,我想出了:

download()
{
    homepage_response=$(curl -s -w "\n%{http_code}" "https://example.com/")
    status_code=$(echo "$homepage_response" | sed -n '$p')
    html=$(echo "$homepage_response" | sed '$d')
    # printf '%s' "${status_code}"
}

start_stopwatch () {
    BEGIN=$(date +%s)
    while true; do
        NOW=$(date +%s)
        DIFF=$(($NOW - $BEGIN))
        MINS=$(($DIFF / 60))
        SECS=$(($DIFF % 60))
        HOURS=$(($DIFF / 3600))

        printf "\rDownload time: %02d:%02d:%02d" "$HOURS" "$MINS" "$SECS"
        sleep 1 & wait  # Make it interruptible
    done
}

start_stopwatch & sw_pid=$!

# # For testing purposes
# echo "$sw_pid"

# Kill background stopwatch if script EXITS beforehand
set -e
kill_sw() {
    kill "$sw_pid"
}
trap kill_sw EXIT

# Call function download()
download

printf "\n"

kill "$sw_pid"

为了预防起见,我添加了set -e,它在脚本在kill "$sw_pid"最后执行之前被中断时调用函数kill_sw()

【问题讨论】:

  • 如果您使用(首选)$((...)) 语法,则不需要 let
  • 您可能想考虑使用curl 已经提供的进度表。
  • 你是对的@chepner 我从我的代码中删除了let
  • 我想避免使用进度表,因为有时我发现它并不完全准确。我只是想实现一个我自己的秒表,它与下载并行执行,最后显示它所花费的时间。 @chepner 我更新了我的问题,你能猜到为什么我的最后一个脚本不起作用吗?

标签: bash shell curl while-loop


【解决方案1】:

我会在后台运行秒表代码,并在 curl 完成后将其终止。

start_stopwatch () {
   SECONDS=0
   while true; do
     now=$SECONDS
     hours=$((now / 3600))
     now=$((now % 3600))
     mins=$((now / 60))
     secs=$((now % 60))

     printf "\rDownload time: %02d:%02d:%02d" "$hours" "$mins" "$secs"
     sleep 1
   done
}

start_stopwatch & sw_pid=$! 
curl ...
kill "$sw_pid"

【讨论】:

  • 我稍微调整了你的代码,否则它就像一个魅力。值得一提的是,对我来说,您的 while 不起作用。它只是返回一个像00:00:00 这样的静态时钟。我用对我有用的while 更新了我的问题。谢谢@chepner!
  • 呃,我的意思是SECONDS,而不是RANDOM。 (还是不行,但我会尝试调试一下。)
  • 好吧,显然你不能本地化$SECONDS,或者它失去了它的特殊能力。只需将其保留为全局变量,如果您关心当前 shell 中 SECONDS 的值,请确保 start_stopwatch 在子 shell 中运行。
【解决方案2】:

您只需要使用函数调用到until 条件。但是当前状态下的函数download() 没有打印状态码的输出。稍作修改如下。

download() {
    homepage_response=$(curl -w "\n%{http_code}" "https://example.com/")    
    status_code=$(echo "$HOMEPAGE_RESPONSE" | sed -n '$p')
    html=$(echo "$HOMEPAGE_RESPONSE" | sed '$d')
    printf '%s' "${status_code}"
}

现在有了打印状态码的函数,在条件 as 中使用它

until [[ "$(download)" -eq "$(curl --output /dev/null --silent --head --fail http://myhost:myport)" ]]; do
    sleep 1
    # Your rest of the script goes here
done

作为一般的编码实践,

  1. 您可以在 let 构造上使用算术运算符 ((..))
  2. 小写用户定义变量以避免与bash 系统特定环境变量发生冲突。
  3. 在考虑移植不涉及任何bash-isms 的脚本时,建议使用不带function 关键字的函数定义。

【讨论】:

  • 嗨@Inian,我根据你的回答更新了我的问题。
  • 我误解了语法吗?
猜你喜欢
  • 1970-01-01
  • 2021-02-11
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
  • 2014-11-17
  • 1970-01-01
  • 2021-03-09
相关资源
最近更新 更多