Як я можу змусити скрипт увійти в окремий файл, скільки разів він був виконаний?


11

Мені потрібно написати сценарій, який записує у файл, скільки разів цей сценарій був виконаний.

Як я можу це зробити?

Відповіді:


15

Я припускаю, що ви хочете мати один файл, countfileякий містить лише одне єдине число, що представляє лічильник виконання.

Ви можете прочитати цей лічильник у змінній оболонки, $counterнаприклад, використовуючи один із цих рядків:

  • read counter < countfile
  • counter=$(cat countfile)

Прості цілісні доповнення можна зробити в самому Bash, використовуючи $(( EXPRESSION ))синтаксис. Потім просто запишіть результат назад до нашого countfile:

echo "$(( counter + 1 ))" > countfile

Вам, ймовірно, слід також захистити свій скрипт для випадку, який countfileще не існує, і створити тоді ініціалізований зі значенням 1.

Вся справа може виглядати приблизно так:

#!/bin/bash
if [[ -f countfile ]] ; then
    read counter < countfile
else
    counter=0
fi
echo "$(( counter + 1 ))" > countfile

2
… Або так:echo $(( $(cat countfile 2>/dev/null || echo 0) + 1 )) > countfile
PerlDuck

1
Приємно, що, здається, працює і @PerlDuck. Я побоювався, що він може відкрити файл для перезапису, перш ніж він буде прочитаний, але, мабуть, синтаксис заміщення процесу, схоже, перешкоджає цьому.
Байт командир

Гарна думка. Я не зовсім впевнений, чи гарантовано $(…)він виконаний раніше, ніж будь-що інше (особливо перед >). Але я часто використовую $(cat countfile 2>/dev/null || echo 0)частину, щоб отримати розумний типовий випадок, якщо файл не існує. Ми можемо додати sponge:-), щоб бути безпечним.
PerlDuck

2
Цю відповідь можна було б покращити, обернувши цей код підрахунку flockкомандою, щоб запобігти умовам перегонів. Дивіться unix.stackexchange.com/a/409276
rrauenza

5

Просто дозвольте скрипту створити файл журналу, додайте, наприклад, рядок у своєму сценарії наприкінці:

echo "Script has been executed at $(date +\%Y-\%m-\%d) $(date +\%H-\%M-\%S)" >> ~/script.log

Таким чином ви можете відформатувати спосіб представлення дати та часу самостійно, але якщо ви просто хочете вказати повну дату та час (і HH:MM:SSце прийнятний формат для вас), ви можете просто використовувати:

echo "Script has been executed at $(date +\%F-\%T)" >> ~/script.log

Тоді ви могли б зробити:

wc -l ~/script.log

Який підраховує символи нового рядка і дає оцінку кількості рядків у файлі журналу. До цього ви можете бачити файл журналу навіть під час його виконання. Щоб адаптувати його під свої потреби, ви можете змінити шляхи та імена, що використовуються для ведення журналу. Я щойно зробив приклад, який зберігає файл журналу ~.

Так, наприклад, ви хочете, щоб сценарій додав цей рахунок до рядка, який ви додали в кінці сценарію, ви можете зробити щось подібне на початку сценарію:

count=$(( $(wc -l ~/script.log | awk '{print $1}') + 1 ))
# the next line can be simply skipped if you not want an output to std_out
echo "Script execution number: $count"

І змініть рядок у кінці сценарію на щось, включаючи навіть цю інформацію:

echo "Script has been executed $count times at $(date +\%F-\%T)" >> ~/script.log

5

Це рішення використовує той самий підхід, що і у відповіді командира байта, але воно не покладається на арифметику оболонки або інші башизми.

exec 2>&3 2>/dev/null
read counter < counter.txt || counter=0
exec 3>&2 3>&-
expr "$counter" + 1 > counter.txt

Перенаправлення потоку

  1. дублювати стандартний потік помилок (2) в інший дескриптор файлу (3),
  2. замініть його (2) перенаправленням на /dev/null(щоб придушити повідомлення про помилку при наступному перенаправлення вводу readкоманди, якщо файл лічильника очікувано відсутній),
  3. пізніше дублювати початковий стандартний потік помилок (зараз на 3) назад на свої місця (2) та
  4. закрити копію стандартного потоку помилок (3).

1

Інший підхід

Окремий файл лічильника має недоліки:

  • На кожен лічильник файлів потрібно 4096 байт (або будь-який розмір блоку).
  • Ви повинні шукати ім'я файлу в скрипті bash, а потім відкрити файл, щоб побачити кількість.
  • Немає блокування файлів (в інших відповідях), тому можливо, що двоє людей оновлюють лічильник точно в той же час (називається умова гонки в коментарях під відповіддю командира байта).

Тож ця відповідь усуває окремий лічильник файлів і ставить рахунок у сам скрипт bash!

  • Введення лічильника в сам скрипт bash дозволяє вам побачити в самому сценарії, скільки разів він був запущений.
  • Використання flockгарантує, що на короткий момент двоє користувачів не можуть одночасно запускати сценарій.
  • Оскільки ім'я лічильника файлів не важко закодовано, вам не потрібно змінювати код для різних сценаріїв, ви можете просто його джерело або скопіювати та вставити з файлу заглушки / котла.

Код

#!/bin/bash

# NAME: run-count.sh
# PATH: $HOME/bin
# DESC: Written for AU Q&A: /ubuntu/988032/how-can-i-cause-a-script-to-log-in-a-separate-file-the-number-of-times-it-has-be

# DATE: Mar 16, 2018.

# This script run count: 0

# ======== FROM HERE DOWN CAN GO INTO FILE INCLUDED WITH SOURCE COMMAND =======

[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
#     This is useful boilerplate code for shell scripts.  Put it at the top  of
#     the  shell script you want to lock and it'll automatically lock itself on
#     the first run.  If the env var $FLOCKER is not set to  the  shell  script
#     that  is being run, then execute flock and grab an exclusive non-blocking
#     lock (using the script itself as the lock file) before re-execing  itself
#     with  the right arguments.  It also sets the FLOCKER env var to the right
#     value so it doesn't run again.

# Read this script with entries separated newline " " into array
mapfile -t ScriptArr < "$0"

# Build search string that cannot be named
SearchStr="This script"
SearchStr=$SearchStr" run count: "

# Find our search string in array and increment count
for i in ${!ScriptArr[@]}; do
    if [[ ${ScriptArr[i]} = *"$SearchStr"* ]]; then
        OldCnt=$( echo ${ScriptArr[i]} | cut -d':' -f2 )
        NewCnt=$(( $OldCnt + 1 ))
        ScriptArr[i]=$SearchStr$NewCnt
        break
    fi
done

# Rewrite our script to disk with new run count
# BONUS: Date of script after writing will be last run time
printf "%s\n" "${ScriptArr[@]}" > "$0"

# ========= FROM HERE UP CAN GO INTO FILE INCLUDED WITH SOURCE COMMAND ========

# Now we return you to your original programming....

exit 0

Інший підхід із використанням файлу журналу

Подібно до відповіді Videonauth, я написав тут відповідь на файл журналу: Сценарій Bash для підтримання аудиту / журналу файлів, для доступу до журналу щоразу, коли використовувались повноваження root geditабо nautilus.

Хоча улов, а не використання gksuсценарію, називається gsuі викликає pkexec"сучасний" спосіб використання sudo в графічному інтерфейсі, тому мені кажуть.

Ще одна перевага полягає не лише в тому, що він говорить щоразу, коли використовуються повноваження root, geditале він реєструє ім'я файлу, який було відредаговано. Ось код.

~/bin/gsu:

#!/bin/bash

# Usage: gsu gedit file1 file2...
#  -OR-  gsu natuilus /dirname

# & is used to spawn process and get prompt back ASAP
# > /dev/null is used to send gtk warnings into dumpster

COMMAND="$1" # extract gedit or nautilus

pkexec "$COMMAND" "${@:2}"

log-file "${@:2}" gsu-log-file-for-"$COMMAND"

/usr/local/bin/log-file:

#! /bin/bash

# NAME: log-file
# PATH: /usr/local/bin
# DESC: Update audit trail/log file with passed parameters.
# CALL: log-file FileName LogFileName
# DATE: Created Nov 18, 2016.
# NOTE: Primarily called from ~/bin/gsu

ABSOLUTE_NAME=$(realpath "$1")
TIME_STAMP=$(date +"%D - %T")
LOG_FILE="$2"

# Does log file need to be created?
if [ ! -f "$LOG_FILE" ]; then
    touch "$LOG_FILE"
    echo "__Date__ - __Time__ - ______File Name______" >> "$LOG_FILE"
    #     MM/DD/YY - hh:mm:ss - "a/b/c/FileName"
fi

echo "$TIME_STAMP" - '"'"$ABSOLUTE_NAME"'"' >> "$LOG_FILE"

exit 0

Зміст файлу журналу gsu-log-file-for-geditпісля кількох редагувань:

__Date__ - __Time__ - ______File Name______
11/18/16 - 19:07:54 - "/etc/default/grub"
11/18/16 - 19:08:34 - "/home/rick/bin/gsu"
11/18/16 - 19:09:26 - "/home/rick/bin/gsu"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.