Jak sprawić, by skrypt logował się w osobnym pliku, ile razy został wykonany?

11

Muszę napisać skrypt, który zapisuje do pliku, ile razy ten skrypt został wykonany.

Jak mogę to zrobić?

Milen Grigorov
źródło

Odpowiedzi:

15

Zakładam, że chcesz mieć jeden plik, countfilektóry zawiera tylko jeden numer reprezentujący licznik wykonania.

Możesz odczytać ten licznik do zmiennej powłoki, $counternp. Używając jednego z następujących wierszy:

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

Proste dodawanie liczb całkowitych można wykonać w samym Bash przy użyciu $(( EXPRESSION ))składni. Następnie po prostu zapisz wynik z powrotem do naszego countfile:

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

Prawdopodobnie powinieneś także zabezpieczyć swój skrypt dla sprawy, countfilektóra jeszcze nie istnieje i utworzyć następnie zainicjowany wartością 1.

Cała sprawa może wyglądać następująco:

#!/bin/bash
if [[ -f countfile ]] ; then
    read counter < countfile
else
    counter=0
fi
echo "$(( counter + 1 ))" > countfile
Bajt Dowódca
źródło
2
… Lub w ten sposób:echo $(( $(cat countfile 2>/dev/null || echo 0) + 1 )) > countfile
PerlDuck
1
Fajnie, wydaje się, że to również działa @PerlDuck. Bałem się, że może otworzyć plik do zastąpienia, zanim zostanie odczytany, ale najwyraźniej składnia zastępowania procesów wydaje się temu zapobiegać.
Bajt Dowódca
Słuszna uwaga. Nie jestem całkowicie pewien, czy jest gwarantowane, że $(…)zostanie wykonany przed czymkolwiek innym (szczególnie przed >). Ale często używam tej $(cat countfile 2>/dev/null || echo 0)części, aby uzyskać rozsądną wartość domyślną na wypadek, gdyby plik nie istniał. Możemy dodać sponge:-), aby być bezpiecznym.
PerlDuck,
2
Ta odpowiedź zostałaby poprawiona poprzez zawinięcie tego kodu liczącego w flockpoleceniu, aby zapobiec warunkom wyścigu. Zobacz unix.stackexchange.com/a/409276
rrauenza
5

Po prostu pozwól skryptowi utworzyć plik dziennika, dodaj na przykład wiersz w skrypcie na końcu:

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

W ten sposób możesz samodzielnie sformatować sposób prezentacji daty i godziny, ale jeśli chcesz po prostu wprowadzić pełną datę i godzinę (i HH:MM:SSjest to format akceptowalny dla Ciebie), możesz również po prostu użyć:

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

Następnie możesz zrobić:

wc -l ~/script.log

Który liczy znaki nowego wiersza i daje szacunkową liczbę wierszy w pliku dziennika. Do tego można zobaczyć w pliku dziennika, nawet gdy został on wykonany. Aby dostosować go do swoich potrzeb, możesz zmienić ścieżki i nazwy używane do rejestrowania. Właśnie zrobiłem tutaj przykład, który zapisuje plik dziennika ~.

Na przykład chcesz, aby skrypt dodał tę liczbę do linii dodanej na końcu skryptu, możesz zrobić coś takiego na początku skryptu:

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"

I zmień wiersz na końcu skryptu na coś zawierającego nawet tę informację:

echo "Script has been executed $count times at $(date +\%F-\%T)" >> ~/script.log
Videonauth
źródło
5

To rozwiązanie wykorzystuje to samo podejście, co odpowiedź Byte Commander, ale nie opiera się na arytmetyce powłoki ani innych Bashizmach.

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

Przekierowania strumienia

  1. zduplikuj standardowy strumień błędów (2) do innego deskryptora pliku (3),
  2. zastąp go (2) przekierowaniem na /dev/null(aby ukryć komunikat o błędzie w kolejnym przekierowaniu wejścia readpolecenia, jeśli plik licznika jest oczekiwany)
  3. później zduplikuj oryginalny standardowy strumień błędów (teraz 3) z powrotem na miejsce (2) i
  4. zamknij kopię standardowego strumienia błędów (3).
David Foerster
źródło
1

Inne podejście

Oddzielny plik licznika ma wady:

  • Zajmuje 4096 bajtów (lub inny rozmiar bloku) dla każdego pliku licznika.
  • Musisz sprawdzić nazwę pliku w skrypcie bash, a następnie otworzyć plik, aby zobaczyć liczbę.
  • Nie ma blokowania plików (w innych odpowiedziach), więc możliwe jest, że dwie osoby zaktualizują licznik dokładnie w tym samym czasie (zwane warunkami wyścigu w komentarzach pod odpowiedzią Byte Commander).

Więc ta odpowiedź eliminuje oddzielny plik licznika i umieszcza licznik w samym skrypcie bash!

  • Umieszczenie licznika w samym skrypcie bash pozwala zobaczyć w samym skrypcie, ile razy został uruchomiony.
  • Korzystanie z flockgwarancji gwarantuje, że przez krótki czas dwóch użytkowników nie będzie mogło uruchomić skryptu jednocześnie.
  • Ponieważ nazwa pliku licznika nie jest zakodowana na stałe, nie musisz zmieniać kodu dla różnych skryptów, możesz po prostu go pobrać lub skopiować i wkleić z pliku pośredniczącego / zbiorczego.

Kod

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

Inne podejście przy użyciu pliku dziennika

Podobnie do odpowiedzi Videonauth napisałem tutaj odpowiedź na plik dziennika: Skrypt Bash do utrzymywania ścieżki audytu / dziennika plików, do których dostęp uzyskuje się do dziennika przy każdym użyciu uprawnień roota za pomocą geditlub nautilus.

Jednak haczyk jest raczej niż używanie gksuskryptu, który jest nazywany gsui wywołuje pkexec„nowoczesny” sposób używania sudo w GUI, więc powiedziano mi.

Kolejną zaletą jest nie tylko to, że za każdym razem używane są uprawnienia roota, geditale także rejestrowanie edytowanej nazwy pliku. Oto kod.

~/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

Zawartość pliku dziennika gsu-log-file-for-geditpo kilku edycjach:

__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"
WinEunuuchs2Unix
źródło