Come posso fare in modo che uno script acceda a un file separato il numero di volte che è stato eseguito?


Risposte:


15

Presumo che tu voglia avere un singolo file countfileche contiene solo un singolo numero che rappresenta il contatore dell'esecuzione.

Puoi leggere questo contatore in una variabile di shell, $counterad esempio usando una di queste righe:

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

È possibile eseguire semplici aggiunte di numeri interi in Bash utilizzando la $(( EXPRESSION ))sintassi. Quindi riscrivi semplicemente il risultato nel nostro countfile:

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

Probabilmente dovresti anche salvaguardare il tuo script per il caso che countfilenon esiste ancora e crearne uno inizializzato con il valore 1 quindi.

L'intera cosa potrebbe apparire così:

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

2
... o così:echo $(( $(cat countfile 2>/dev/null || echo 0) + 1 )) > countfile
PerlDuck il

1
Bello, sembra funzionare anche @PerlDuck. Temevo che potesse aprire il file per la sovrascrittura prima che venisse letto, ma a quanto pare la sintassi della sostituzione del processo sembra impedirlo.
Byte Commander

Buon punto. Non sono perfettamente sicuro che sia garantito che $(…)venga eseguito prima di ogni altra cosa (specialmente prima del >). Ma uso spesso la $(cat countfile 2>/dev/null || echo 0)parte per ottenere un valore predefinito ragionevole nel caso in cui il file non esista. Potremmo aggiungere un sponge:-) per essere al sicuro.
PerlDuck,

2
Questa risposta sarebbe migliorata racchiudendo quel codice di conteggio in un flockcomando per prevenire le condizioni di gara. Vedi unix.stackexchange.com/a/409276
rrauenza il

5

Lascia che lo script crei un file di registro, aggiungi ad esempio una riga nello script alla fine:

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

In questo modo puoi formattare tu stesso il modo in cui presenti la data e l'ora, ma se vuoi semplicemente andare con una data e un'ora complete (ed HH:MM:SSè un formato accettabile per te) puoi anche semplicemente usare:

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

Quindi potresti fare:

wc -l ~/script.log

Che conta i caratteri di nuova riga e ti dà una stima di quante righe ci sono nel file di registro. Fino a questo puoi vedere nel file di registro anche quando è stato eseguito. Per adattarlo alle proprie esigenze, è possibile modificare i percorsi e i nomi utilizzati per la registrazione. Ho appena fatto un esempio qui che salva il file di log ~.

Quindi, ad esempio, se vuoi che lo script aggiunga questo conteggio alla riga che hai aggiunto alla fine dello script, potresti fare qualcosa del genere all'inizio dello script:

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"

E cambia la tua linea alla fine dello script in qualcosa che includa anche quelle informazioni:

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

5

Questa soluzione utilizza lo stesso approccio della risposta di Byte Commander ma non si basa sull'aritmetica della shell o su altri Bashismi.

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

I reindirizzamenti dello stream

  1. duplicare il flusso di errori standard (2) in un descrittore di file diverso (3),
  2. sostituirlo (2) con un reindirizzamento a /dev/null(per sopprimere il messaggio di errore nel successivo reindirizzamento dell'input del readcomando se il file del contatore manca in modo previsto),
  3. in seguito duplicare il flusso di errori standard originale (ora in 3) nuovamente in posizione (2) e
  4. chiudere la copia del flusso di errori standard (3).

1

Un approccio diverso

Un file contatore separato presenta degli svantaggi:

  • Ci vogliono 4096 byte (o qualunque sia la dimensione del blocco) per ogni file contatore.
  • Devi cercare il nome del file nello script bash e quindi aprire il file per vedere il conteggio.
  • Non esiste il blocco dei file (in altre risposte), quindi è possibile che due persone aggiornino il contatore nello stesso momento (chiamato condizione di gara nei commenti sotto la risposta di Byte Commander).

Quindi questa risposta elimina un file contatore separato e inserisce il conteggio nello stesso script bash!

  • Mettere il contatore nello script bash stesso ti permette di vedere all'interno del tuo script stesso quante volte è stato eseguito.
  • L'uso flockgarantisce che per un breve momento non è possibile per due utenti eseguire lo script contemporaneamente.
  • Poiché il nome del file del contatore non è codificato, non è necessario modificare il codice per diversi script, è possibile semplicemente estrarlo o copiarlo e incollarlo da un file stub / boilerplate.

Il codice

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

Un altro approccio che utilizza un file di registro

Simile alla risposta di Videonauth, ho scritto una risposta per il file di registro qui: script Bash per mantenere la traccia di controllo / registro dei file a cui si accede ogni volta che si usano i poteri di root con gedito nautilus.

Il problema è che piuttosto che usare gksulo script è chiamato gsue invoca pkexecil modo "moderno" di usare sudo nella GUI, così mi viene detto.

Un altro vantaggio non è solo dire ogni volta che sono stati utilizzati i poteri di root, geditma registra il nome del file che è stato modificato. Ecco il codice

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

Contenuto del file di registro gsu-log-file-for-geditdopo alcune modifiche:

__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"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.