【问题标题】:Bash loop taking hours to loop after about waiting a few hours between each run在每次运行之间大约等待几个小时后,Bash 循环需要几个小时才能循环
【发布时间】:2019-02-22 23:03:22
【问题描述】:

说明

你好,

我有一个 bash 脚本,它通过在 /usr/local/bin/ 中找到某些命令并执行它们来循环它们。命令给出的输出只是数字,我们获取这些数字,将它们存储在文件中,如果它们之间的时间差超过半小时,则将当前输出与存储在文件中的输出进行比较。如果没有,那么就不要比较,只打印最近的数字。

这是我的脚本;

#!/bin/bash

warning="\e[93m%-17s%-12s%-12s\n"
danger="\e[91m%-17s%-12s%-12s\n"
green="\e[92m%-17s%-12s%-12s\n"
normal="%-17s%-12s%-12s\n"

wallets=/usr/local/bin/*-cli

current_date=$(date +%s)

for i in $wallets; do
    current_blocks=`$i getblockcount`
    coin_name=`basename $i -cli`

    if [ ! "$line" ]; then
        read line
        set -- $line
    fi

    if [ "$1" == "$coin_name" ]; then
        line='' # read new line next time
        time_difference=$(( $current_date - $3 ))

            if (( time_difference > 3600 )); then
                if (( current_blocks > $2 )); then
                    printf "$green" $coin_name $current_blocks $2
                    sed -i '' "/$1/c$1 $current_blocks $current_date" blocks.log
                    tput sgr0
                else
                    printf "$danger" $coin_name $current_blocks $2
                    sleep 2
                    tput sgr0
                fi
            elif (( time_difference > 1800 )); then
                if (( current_blocks > $2 )); then
                    printf "$green" $coin_name $current_blocks $2
                    sed -i '' "/$1/c$1 $current_blocks $current_date" blocks.log
                    tput sgr0
                else
                    printf "$warning" $coin_name $current_blocks $2
                    sleep 2
                    tput sgr0
                fi
            else
                printf "$normal" $coin_name $current_blocks $2
                tput sgr0
            fi

    else
            # $line as well as $1 and $2 remain set, will be used in next loop cycle
            if [ ! "$1" ]; then         # $1 is empty at EOF
                # create a new blocks.log to not disturb reading from original:
                cp blocks.log $$; mv $$ blocks.log
                echo "$coin_name $current_blocks $current_date" >>blocks.log
            else
                # insert before current line (address /$1/)
                sed -i '' "/$1/i$coin_name $current_blocks $current_date" blocks.log
            fi
    fi

done <blocks.log

这是blocks.log 文件的示例输出;

# first one is the $coin_name second one is $current_blocks and the third one is unix time.
bitcoin 99983 1550742031
litecoin 138432 1550742031
dash 130994 1550742031

当您运行脚本时,它会显示与blocks.log 文件类似的输出,并根据脚本中的if-else 条件在blocks.log 文件中修改它们。

问题

这个脚本的问题是,当我最初运行它几次时,它工作得很好,几乎可以立即循环,但是随着我越来越多地运行脚本或者只是运行它几次然后等待大约 5-6 小时,然后再次运行脚本,它需要几乎几个小时来循环,考虑到只有几个 $wallets 并且当您运行 $i getblockcount 命令时它们会立即为您提供输出,这应该是即时的所以我不知道为什么一段时间后运行脚本需要几个小时。

我该如何解决这个问题?

【问题讨论】:

  • 你正在同时读取和写入文件blocks.log - 这不健康。不确定这是否是问题,但你应该明确地解决它。您可能还想看看shellcheck.net
  • 谢谢,我去看看。我不知道如何以其他方式而不是同时阅读和写作。
  • 1.计算新值并将它们写入变量或临时文件。 2. 最后用新文件覆盖旧文件。
  • @Socowi 你介意用示例脚本回答这个问题吗?对不起,我是 bash 的新手,所以..
  • @Armali 看起来它没有挂起。我发现脚本正在退出,因为在某些情况下,$2s 之一被删除。它只是随机删除,所以我猜是因为同时读/写而发生了冲突。

标签: bash


【解决方案1】:

不确定是否准确阅读,但您可以查看time 命令,man time 用于描述。

warning="\e[93m%-17s%-12s%-12s\n"
danger="\e[91m%-17s%-12s%-12s\n"
green="\e[92m%-17s%-12s%-12s\n"
normal="%-17s%-12s%-12s\n"

wallets=/usr/local/bin/*-cli

current_date=$(date +%s)

for i in $wallets; do

{ time (
    current_blocks=`$i getblockcount`
    coin_name=`basename $i -cli`

    { time (
    if [ ! "$line" ]; then
        read line
        set -- $line
    fi
    ) } > wallet-$i-lineread.log 2>&1 

    if [ "$1" == "$coin_name" ]; then
        line='' # read new line next time
        time_difference=$(( $current_date - $3 ))

            if (( time_difference > 3600 )); then
                if (( current_blocks > $2 )); then
                    printf "$green" $coin_name $current_blocks $2
                    sed -i '' "/$1/c$1 $current_blocks $current_date" blocks.log
                    tput sgr0
                else
                    printf "$danger" $coin_name $current_blocks $2
                    sleep 2
                    tput sgr0
                fi
            elif (( time_difference > 1800 )); then
                if (( current_blocks > $2 )); then
                    printf "$green" $coin_name $current_blocks $2
                    sed -i '' "/$1/c$1 $current_blocks $current_date" blocks.log
                    tput sgr0
                else
                    printf "$warning" $coin_name $current_blocks $2
                    sleep 2
                    tput sgr0
                fi
            else
                printf "$normal" $coin_name $current_blocks $2
                tput sgr0
            fi

    else
            # $line as well as $1 and $2 remain set, will be used in next loop cycle
            if [ ! "$1" ]; then         # $1 is empty at EOF
                # create a new blocks.log to not disturb reading from original:
                cp blocks.log $$; mv $$ blocks.log
                echo "$coin_name $current_blocks $current_date" >>blocks.log
            else
                # insert before current line (address /$1/)
                sed -i '' "/$1/i$coin_name $current_blocks $current_date" blocks.log
            fi
    fi
) } > $i-runtime.log 2>&1 
done <blocks.log

this post 相似的重要部分是我在上面添加的两个示例的代码块(不确定您要对哪些组进行计时,或者是否有个别命令。

{ time ( BLOCK; OF; MANY; COMMANDS; ) } > $i-runtime.log 2>&1 

分解这个命令

  • { time 创建一个打开我们要计时的代码块
  • ( 创建命令数组
  • BLOCK OF COMMANDS 不言自明
  • )}特写块
  • &gt; 重定向到文件
  • $i-runtime.log 将它放在哪个文件中,您需要一个环境变量或一些基于块的有用名称
  • 2&gt;&amp;1 重定向 STDOUT 和 STDERR,需要将时间输出放入文件中。

您可能知道您可以将&gt; 替换为&gt;&gt; 以将运行的输出保存到同一日志文件中。

【讨论】:

  • 这里的想法是如果$current_blocks 大于$3&gt;&gt; 将替换blocks.log 文件的行,而&gt;&gt; 将添加新的行,这实际上是不需要的,因为我们不需要如果满足我提到的条件,则不需要旧日志。
【解决方案2】:

它只是随机删除,所以我猜是因为同时读/写而发生了冲突。

如果在那里使用了sed(顺便说一句,这是一个奇怪的问题,因为它具有FreeBSD语法-i <i>extension</i>-i之后有空格,而不是Ubuntu GNU语法-i[SUFFIX]没有空格)确实应该导致这样冲突,您可以通过阅读blocks.log 的副本来避免它们,例如。 g.:

cp blocks.log blocks.old
for i in $wallets; do
    …
done <blocks.old

那么你也可以删除cp blocks.log $$; mv $$ blocks.log这一行——它不再需要了。


问题是脚本在每次运行时都不会返回$current_blocks。我不知道为什么,但它以某种方式错过了它。它应该运行这个命令$i getblockcount,它将返回一个数字。它正在运行命令但没有得到数字。

您必须决定在这种情况下该怎么做;一种可能性是在当前运行中跳过这个钱包,例如。 G。用这个:

    if [ "$1" == "$coin_name" ]; then
        line='' # read new line next time
        if [ ! "$current_blocks" ]; then
            echo $i failed to return blockcount
            continue
        fi
        …
    else
        if [ ! "$current_blocks" ]; then
            echo $i failed to return blockcount
            continue
        fi
        …

不幸的是,我们不能轻易地将相同的if 块放在外部if 之外,因为line='' 必须在我们continue 之前执行。

【讨论】:

  • 这仍然没有解决您从我的评论中引用的问题。
  • @Marry Jane - 那么原因肯定不同,因为有了这个,就不能再有同时读/写了。 - 有一个-cli 文件在运行之间从/usr/local/bin/ 中删除? (脚本没有考虑到这一点。)
  • 好吧,实际上并没有。问题是脚本不会在每次运行时返回$current_blocks。我不知道为什么,但它以某种方式错过了它。它应该运行这个命令$i getblockcount,它将返回一个数字。它正在运行命令但没有得到数字。
  • @Marry Jane - 这是一个重要的观察结果。我在答案中添加了一个建议。
猜你喜欢
  • 2019-11-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2016-12-02
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
  • 1970-01-01
相关资源
最近更新 更多