Wie kann ich veranlassen, dass sich ein Skript so oft in einer separaten Datei anmeldet, wie es ausgeführt wurde?

11

Ich muss ein Skript schreiben, das in eine Datei schreibt, wie oft dieses Skript ausgeführt wurde.

Wie kann ich das machen?

Milen Grigorov
quelle

Antworten:

15

Ich gehe davon aus, dass Sie eine einzelne Datei haben möchten, die countfilenur eine einzige Nummer enthält, die den Ausführungszähler darstellt.

Sie können diesen Zähler in eine Shell-Variable einlesen, $counterz. B. mit einer der folgenden Zeilen:

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

Einfache Ganzzahladditionen können in Bash selbst mithilfe der $(( EXPRESSION ))Syntax vorgenommen werden. Dann schreiben Sie einfach das Ergebnis zurück an countfile:

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

Sie sollten Ihr Skript wahrscheinlich auch für den Fall schützen, der countfilenoch nicht vorhanden ist, und dann ein Skript erstellen, das mit dem Wert 1 initialisiert wurde.

Das Ganze könnte so aussehen:

#!/bin/bash
if [[ -f countfile ]] ; then
    read counter < countfile
else
    counter=0
fi
echo "$(( counter + 1 ))" > countfile
Byte Commander
quelle
2
… Oder so:echo $(( $(cat countfile 2>/dev/null || echo 0) + 1 )) > countfile
PerlDuck
1
Schön, das scheint auch @PerlDuck zu funktionieren. Ich hatte Angst, dass es die Datei zum Überschreiben öffnen könnte, bevor sie gelesen wird, aber anscheinend scheint die Syntax der Prozessersetzung dies zu verhindern.
Byte Commander
Guter Punkt. Ich bin mir nicht ganz sicher, ob es garantiert ist, dass es $(…)vor irgendetwas anderem ausgeführt wird (besonders vor dem >). Aber ich benutze das $(cat countfile 2>/dev/null || echo 0)Teil oft , um einen vernünftigen Standard zu erhalten, falls die Datei nicht existiert. Wir könnten ein sponge:-) hinzufügen , um sicher zu gehen.
PerlDuck
2
Diese Antwort würde verbessert, indem dieser Zählcode in einen flockBefehl eingeschlossen wird, um Rennbedingungen zu verhindern. Siehe unix.stackexchange.com/a/409276
rrauenza
5

Lassen Sie das Skript einfach eine Protokolldatei erstellen und fügen Sie am Ende beispielsweise eine Zeile in Ihr Skript ein:

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

Auf diese Weise können Sie die Darstellung von Datum und Uhrzeit selbst formatieren. Wenn Sie jedoch nur ein vollständiges Datum und eine vollständige Uhrzeit verwenden möchten (und HH:MM:SSein für Sie akzeptables Format haben), können Sie auch einfach Folgendes verwenden:

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

Dann könnten Sie tun:

wc -l ~/script.log

Dies zählt die Zeilenumbruchzeichen und gibt Ihnen eine Schätzung, wie viele Zeilen sich in der Protokolldatei befinden. Bis dahin können Sie in der Protokolldatei sehen, auch wenn sie ausgeführt wurde. Um es an Ihre Bedürfnisse anzupassen, können Sie die Pfade und Namen ändern, die für die Protokollierung verwendet werden. Ich habe hier nur ein Beispiel gemacht, in dem die Protokolldatei gespeichert wird ~.

Wenn Sie beispielsweise möchten, dass das Skript diese Anzahl zu der Zeile hinzufügt, die Sie am Ende Ihres Skripts hinzugefügt haben, können Sie am Anfang Ihres Skripts Folgendes tun:

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"

Und ändern Sie Ihre Zeile am Ende des Skripts in etwas, das auch diese Informationen enthält:

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

Diese Lösung verwendet den gleichen Ansatz wie die Antwort von Byte Commander, basiert jedoch nicht auf Shell-Arithmetik oder anderen Bashismen.

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

Die Stream-Umleitungen

  1. Dupliziere den Standardfehlerstrom (2) in einen anderen Dateideskriptor (3).
  2. Ersetzen Sie es (2) durch eine Umleitung zu /dev/null(um die Fehlermeldung bei der nachfolgenden Umleitung der readBefehlseingabe zu unterdrücken, wenn die Zählerdatei erwartungsgemäß fehlt).
  3. Duplizieren Sie später den ursprünglichen Standardfehlerstrom (jetzt bei 3) wieder an seinen Platz (2) und
  4. Schließen Sie die Kopie des Standardfehlerstroms (3).
David Foerster
quelle
1

Ein anderer Versuch

Eine separate Zählerdatei hat Nachteile:

  • Für jede Zählerdatei werden 4096 Bytes (oder eine beliebige Blockgröße) benötigt.
  • Sie müssen den Namen der Datei im Bash-Skript nachschlagen und dann die Datei öffnen, um die Anzahl anzuzeigen.
  • Es gibt keine Dateisperrung (in anderen Antworten), so dass es möglich ist, dass zwei Personen den Zähler genau zur gleichen Zeit aktualisieren (in den Kommentaren unter der Antwort von Byte Commander als Rennbedingung bezeichnet).

Diese Antwort beseitigt also eine separate Zählerdatei und fügt die Zählung in das Bash-Skript selbst ein!

  • Wenn Sie den Zähler in das Bash-Skript selbst einfügen, können Sie in Ihrem Skript selbst sehen, wie oft es ausgeführt wurde.
  • Die Verwendung flockgarantiert, dass es für einen kurzen Moment nicht möglich ist, dass zwei Benutzer das Skript gleichzeitig ausführen.
  • Da der Name der Zählerdatei nicht fest codiert ist, müssen Sie den Code für verschiedene Skripte nicht ändern. Sie können ihn einfach als Quelle verwenden oder aus einer Stub- / Boilerplate-Datei kopieren und einfügen.

Der Code

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

Ein anderer Ansatz unter Verwendung einer Protokolldatei

Ähnlich wie bei Videonauths Antwort habe ich hier eine Antwort auf eine Protokolldatei geschrieben: Bash-Skript zum Verwalten des Prüfpfads / Protokolls von Dateien, auf die jedes Mal zugegriffen wird , wenn bei geditoder mit Root-Potenzen mit oder protokolliert wird nautilus.

Der Haken ist jedoch, anstatt gksudas Skript zu verwenden, benannt zu sein gsuund pkexecdie "moderne" Art der Verwendung von sudo in der GUI aufzurufen, so wird mir gesagt.

Ein weiterer Vorteil ist, dass nicht nur jedes Mal angegeben wird, wenn Root-Kräfte verwendet wurden, geditsondern auch der bearbeitete Dateiname protokolliert wird. Hier ist der Code.

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

Inhalt der Protokolldatei gsu-log-file-for-geditnach einigen Änderungen:

__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
quelle