Bash通过循环操作并使用数组对文件内容进行排序

时间:2019-02-04 01:07:39

标签: arrays bash for-loop while-loop

目的

创建一个循环执行某些命令的bash脚本,并将每个命令的输出(它们仅打印数字)保存到文件(我猜最好的方法是将它们保存在文件?),并在每个输出旁边加上日期(unix时间),以便下次运行脚本并再次遍历脚本时可以使用这些存储的值,看看是否没有最近一个小时内命令输出的任何变化。

示例输出

# ./script
command1 123123
command2 123123

重要提示

  • 该脚本将循环执行约200条命令。
  • 将来会有新的命令,因此脚本将必须检查此命令是否存在于已保存的文件中。如果已经存在,请仅在最后一个小时内进行比较,以查看自上次保存文件以来该数字是否已更改。如果不存在,请将其保存到文件中,以便下次使用它进行比较。
  • 随着命令的增加/减少/更改,脚本将运行的命令顺序可能会有所不同。因此,如果现在只是这样;
# ./script
command1 123123
command2 123123

例如,您将来添加第三条命令时,顺序可能会更改(也不确定是遵循哪种模式);

# ./script
command1 123123
command3 123123
command2 123123

例如,我们不能逐行读取它,在这种情况下,我认为最好的方法是将它们与command*名称进行比较。

存储值的结构

我对存储值的假定结构是这样的(不必坚持这样);

command1 123123 unixtime
command2 123123 unixtime

关于上述命令

我所说的commands基本上是运行在/usr/local/bin/上的应用程序,可以直接在外壳程序上运行它们的名称来访问它们,例如command1 getnumber,它将为您显示数字

由于命令位于/usr/local/bin/中,并且遵循类似的模式,因此我首先循环访问/usr/local/bin/的{​​{1}}。见下文。

command*

因此,这将循环浏览以commands=`find /usr/local/bin/ -name 'command*'` for i in $commands; do echo "$i" "`$i getnumber`" done 开头的所有文件,并为每个文件运行command,这将打印出我们所需的数字。

现在,我们需要将这些值存储在文件中,以在下次运行命令时进行比较。

捕获:

我们甚至可能每隔几分钟运行一次脚本,但是如果(数字)的值在过去一小时内没有更改,我们只需报告

该脚本将在您每次运行时列出数字,我们可能会向最近一小时未更改的用户添加样式,以使其醒目,例如添加一个给他们红色?

尝试编号1

所以这是我第一次构建此脚本。这是它的样子;

command* getnumber

那是一场灾难,最终,当我运行它时它什么也没做。

尝试编号2

这次,我尝试了另一种方法将#!/bin/bash commands=`find /usr/local/bin/ -name 'command*'` date=`date +%s` while read -r command number unixtime; do for i in $commands; do current_block_count=`$i getnumber` if [[ $command = $i ]]; then echo "$i exists in the file, checking the number changes within last hour" # just for debugging, will be removed in production if (( ($date-$unixtime)/60000 > 60 )); then if (( $number >= $current_number_count )); then echo "There isn't a change within the last hour, this is a problem!" # just for debugging, will be removed in production echo -e "$i" "`$i getnumber`" "/" "$number" "\e[31m< No change within last hour." else echo "$i" "`$i getnumber`" echo "There's a change within the last hour, we're good." # just for debugging, will be removed in production # find the line number of $i so we can change it with the new output line_number=`grep -Fn '$i' outputs.log` new_output=`$i getnumber` sed -i "$line_numbers/.*/$new_output/" outputs.log fi else echo "$i" "`$i getnumber`" # find the line number of $i so we can change it with the new output line_number=`grep -Fn '$i' outputs.log` output_check="$i getnumber; date +%s" new_output=`eval ${output_check}` sed -i "$line_numbers/.*/$new_output/" outputs.log fi else echo "$i does not exists in the file, adding it now" # just for debugging, will be removed in production echo "$i" "`$i getnumber`" "`date +%s`" >> outputs.log fi done done < outputs.log 嵌套在for loop之外。

while loop

不幸的是,我再也没有运气了。

有人可以帮我吗?

附加说明#2

因此,基本上,您是第一次运行脚本,#!/bin/bash commands=`find /usr/local/bin/ -name 'command*'` date=`date +%s` for i in $commands; do echo "${i}" "`$i getnumber`" name=${i} number=`$i getnumber` unixtime=$date echo "$name" "$number" "$unixtime" # just for debugging, will be removed in production while read -r command number unixtime; do if ! [ -z ${name+x} ]; then echo "$name" "$number" "$unix" >> outputs.log else if [[ $name = $i ]]; then if (( ($date-$unixtime)/60000 > 60 )); then if (( $number >= $current_number_count )); then echo "There isn't a change within the last hour, this is a problem!" # just for debugging, will be removed in production echo -e "$i" "`$i getnumber`" "/" "$number" "\e[31m< No change within last hour." else echo "$i" "`$i getnumber`" echo "There's a change within the last hour, we're good." # just for debugging, will be removed in production # find the line number of $i so we can change it with the new output line_number=`grep -Fn '$i' outputs.log` new_output=`$i getnumber` sed -i "$line_numbers/.*/$new_output/" outputs.log fi else echo "$i" "`$i getnumber`" # find the line number of $i so we can change it with the new output line_number=`grep -Fn '$i' outputs.log` output_check="$i getnumber; date +%s" new_output=`eval ${output_check}` sed -i "$line_numbers/.*/$new_output/" outputs.log fi else echo "$i does not exists in the file, adding it now" # just for debugging, will be removed in production echo "$i" "`$i getnumber`" "`date +%s`" >> outputs.log fi fi done < outputs.log done 为空,因此将命令的输出写入outputs.log

已经过了10分钟,您又重新运行了脚本,因为只有10分钟并且不超过一个小时,所以脚本不会检查数字是否已更改。它不会操纵存储的值,但每次运行时都会向我们显示命令的输出。 (它们当前的输出,而不是存储的值)

例如,在这10分钟内,可能已经添加了新命令,因此它将在每次运行脚本时检查是否存储了命令的输出,只是为了处理新命令。

现在已经过去了,比方说1.2小时过去了,您决定再次运行该脚本,这一次脚本将检查一个多小时后数字是否没有变化,并报告说outputs.log < / p>

简单说明

  • 您有100个要运行的命令,您的脚本将循环遍历每个命令,并对每个命令执行以下操作;
  • 随时运行脚本
  • 每次运行时,检查Hey! It's been more than an hour passed and those numbers still haven't changed, there might be problem!是否包含命令
    • 如果outputs.log包含每个循环的命令,请检查每个循环的最后存储日期($ unixtime)。
      • 如果上次存储的日期超过一个小时,请检查当前运行与存储值之间的数字
        • 如果数字未更改超过一个小时,请以红色文字运行命令。
        • 如果数字已更改,请像往常一样运行该命令,而不会发出任何警告。
      • 如果上次存储的日期少于一个小时,请照常运行命令。
    • 如果outputs.log不包含命令,只需将它们存储在文件中,以便下次运行时进行检查。

1 个答案:

答案 0 :(得分:0)

以下内容使用sqlite数据库而非平面文件来存储结果,从而使查询先前运行的历史记录变得容易:

#!/bin/sh

database=tracker.db

if [ ! -e "$database" ]; then
    sqlite3 -batch "$database" <<EOF
CREATE TABLE IF NOT EXISTS outputs(command TEXT
                                 , output INTEGER
                                 , ts INTEGER NOT NULL DEFAULT (strftime('%s', 'now')));
CREATE INDEX IF NOT EXISTS outputs_idx ON outputs(command, ts);
EOF
fi

for cmd in /usr/local/bin/command*; do
    f=$(basename "$cmd")
    o=$("$cmd")
    echo "$f $o"
    sqlite3 -batch "$database" <<EOF
INSERT INTO outputs(command, output) VALUES ('$f', $o);
SELECT command || ' has unchanged output!'
FROM outputs
WHERE command = '$f' AND ts >= strftime('%s', 'now', '-1 hour')
GROUP BY command
HAVING count(DISTINCT output) = 1 AND count(*) > 1;
EOF
done

它列出了最近一个小时内每次运行的命令产生相同的输出(并跳过仅运行一次的命令)。相反,如果您对每个命令的最新输出与该小时时间范围内的上一次运行相同的情况感兴趣,请使用以下命令替换循环中的sqlite3调用:

sqlite3 -batch $database <<EOF
INSERT INTO outputs(command, output) VALUES ('$f', $o);
WITH prevs AS
 (SELECT command
       , output
       , row_number() OVER w AS rn
       , lead(output, 1) OVER w AS prev
  FROM outputs
  WHERE command = '$f' AND ts >= strftime('%s', 'now', '-1 hour')
  WINDOW w AS (ORDER BY ts DESC))
SELECT command || ' has unchanged output!'
FROM prevs
WHERE output = prev AND rn = 1;
EOF

(这需要版本3.25或更高版本的sqlite3 shell,因为它使用了随后引入的功能。)