Bash манипулирует и сортирует содержимое файла с массивами через цикл - PullRequest
0 голосов
/ 04 февраля 2019

Цель

Создать скрипт 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/ для command*.См. Ниже.

commands=`find /usr/local/bin/ -name 'command*'`

for i in $commands; do
    echo "$i" "`$i getnumber`"
done

, поэтому он будет циклически перебирать все файлы, которые начинаются с command, и будет запускать command* getnumber для каждого, что выведет нужные нам числа.

Сейчаснам нужно сохранить эти значения в файле, чтобы сравнить их при следующем запуске команды.

Catch:

Мы можем даже запускать скрипт каждые несколько минут, но нам нужно только сообщить, что значения (числа) не изменились за последний час .

Сценарий будет перечислять числа каждый раз, когда вы его запускаете, и мыможет добавить стиль для тех, кто не изменился за последний час, чтобы вытащить их для глаз, например, добавить к ним красный цвет?

Номер попытки # 1

Так вотмоя первая попытка построить этот скриптВот как это выглядит:

#!/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

Это была настоящая катастрофа и, в конце концов, она ничего не сделала, когда я ее запустил.

Номер попытки # 2

На этот раз я попробовал другой подход, вложив for loop за пределы while loop.

#!/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

К сожалению, мне опять не повезло.

Может кто-нибудь помочь мнеhand?

Дополнительные примечания # 2

Таким образом, вы запускаете скрипт в первый раз, outputs.log пуст, поэтому вы записываете выходные команды в outputs.log.

Прошло 10 минут, вы снова запускаете скрипт, так как прошло всего 10 минут и не более часа, скрипт не будет проверять, изменились ли числа или нет.Он не будет манипулировать сохраненными значениями, но также будет отображать нам выходные данные команды каждый раз, когда вы ее запускаете. (их текущие выходные данные, а не из сохраненных значений)

Например, в этот 10-минутный период, возможно, были добавлены новые команды, поэтому он проверит, сохранены ли выходные данные командкаждый раз, когда вы запускаете скрипт, просто для работы с новыми командами.

Теперь прошло, скажем, 1,2 часа, вы решили снова запустить сценарий, на этот раз сценарий проверит, не изменились ли числа через час, и сообщит нам, что Hey! It's been more than an hour passed and those numbers still haven't changed, there might be problem!

Простое объяснение

  • У вас есть 100 команд для запуска, ваш скрипт будет циклически проходить по каждой из них и будет выполнять следующие действия для каждой;
  • Запускать скрипт в любое время
  • При каждом запуске проверьте, содержит ли outputs.log команду
    • Если outputs.log содержит команды каждого цикла, проверьте последнюю сохраненную дату ($ unixtime) каждой из них.
      • Если последняя сохраненная дата превышает час, проверьте числа между текущим прогоном и сохраненным значением
        • Если числа не изменились более часа, выполните команду вкрасный цвет текста.
        • Если числа изменились, выполните команду как обычно без предупреждения.
      • Если последняя сохраненная дата меньше часа, выполните командукак обычно.
    • Если outputs.log не содержит команду, просто сохраните их в файле, чтобы ее можно было использовать для последующих проверок.

1 Ответ

0 голосов
/ 04 февраля 2019

Следующее использует базу данных 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

(для этого требуетсяsqlite3 оболочка из выпуска 3.25 или новее, поскольку она использует функции, представленные тогда.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...