Como posso fazer com que um script faça login em um arquivo separado o número de vezes que ele foi executado?

10

Eu preciso escrever um script que grave em um arquivo quantas vezes esse script foi executado.

Como posso fazer isso?

    
por Milen Grigorov 20.12.2017 / 14:43

4 respostas

14

Suponho que você queira ter um único arquivo countfile que contenha apenas um único número representando o contador de execução.

Você pode ler esse contador em uma variável de shell $counter , por exemplo. usando uma destas linhas:

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

Adições inteiras simples podem ser feitas no próprio Bash usando a sintaxe $(( EXPRESSION )) . Em seguida, basta escrever o resultado de volta ao nosso countfile :

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

Você provavelmente também deve proteger seu script para o caso em que countfile ainda não existe e criar um inicializado com o valor 1.

A coisa toda pode ser assim:

#!/bin/bash
if [[ -f countfile ]] ; then
    read counter < countfile
else
    counter=0
fi
echo "$(( counter + 1 ))" > countfile
    
por Byte Commander 20.12.2017 / 14:51
5

Apenas deixe o script criar um arquivo de log, adicione por exemplo uma linha no seu script no final:

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

Dessa forma, você pode formatar a maneira de apresentar a data e a hora, mas se quiser simplesmente ir com uma data e hora completas (e HH:MM:SS é um formato aceitável para você), você também pode usar:

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

Então você poderia fazer:

wc -l ~/script.log

Que conta os caracteres de nova linha e fornece uma estimativa de quantas linhas estão dentro do arquivo de log. Até que você pode ver dentro do arquivo de log, mesmo quando foi executado. Para adaptá-lo às suas necessidades, você pode alterar os caminhos e nomes usados para o registro. Eu apenas fiz um exemplo aqui que salva o logfile em ~ .

Então, por exemplo, você quer que o script adicione essa contagem à linha que você adicionou no final do seu script. Você poderia fazer algo assim no início do seu 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 altere sua linha no final do script para algo que inclua até mesmo essa informação:

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

Esta solução usa a mesma abordagem da Resposta do Byte Commander , mas não depende da aritmética de shell ou de outros Bashisms.

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

Os redirecionamentos de fluxo

  1. duplique o fluxo de erro padrão (2) para um descritor de arquivo diferente (3),
  2. substitua-o (2) por um redirecionamento para /dev/null (para suprimir a mensagem de erro no redirecionamento subseqüente da entrada do comando read , se o arquivo do contador estiver ausente do esperado),
  3. depois duplique o fluxo de erro padrão original (agora em 3) de volta ao lugar (2) e
  4. feche a cópia do fluxo de erro padrão (3).
por David Foerster 21.12.2017 / 00:39
1

Uma abordagem diferente

Um arquivo de contador separado tem desvantagens:

  • São necessários 4096 bytes (ou seja qual for o tamanho do bloco) para cada arquivo do contador.
  • Você precisa procurar o nome do arquivo no script bash e abrir o arquivo para ver a contagem.
  • Não há bloqueio de arquivo (em outras respostas), portanto é possível que duas pessoas atualizem o contador ao mesmo tempo (chamada condição de corrida nos comentários na resposta do Byte Commander).

Portanto, esta resposta elimina um arquivo contador separado e coloca a contagem no próprio script bash!

  • Colocar o contador no próprio script permite que você veja dentro do próprio script quantas vezes ele foi executado.
  • O uso de flock garante que, por um breve momento, não seja possível que dois usuários executem o script ao mesmo tempo.
  • Como o nome do arquivo do contador não é codificado, você não precisa alterar o código de scripts diferentes, basta fazer a sua fonte ou copiá-lo e colá-lo em um arquivo stub / boilerplate.

O código

#!/bin/bash

# NAME: run-count.sh
# PATH: $HOME/bin
# DESC: Written for AU Q&A: https://sobrelinux.info/questions/15056/how-can-i-cause-a-script-to-log-in-a-separate-file-the-number-of-times-it-has-be"${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

Outra abordagem usando um arquivo de log

Semelhante à resposta da Videonauth, escrevi uma resposta de arquivo de log aqui: Script de bash para manter trilha de auditoria / log de arquivos acessados para registrar todas as vezes que os poderes de root foram usados com gedit ou nautilus .

A captura é, em vez de usar gksu , o script chama-se gsu e invoca pkexec a maneira "moderna" de usar o sudo na GUI, por isso me disseram.

Outra vantagem não é apenas dizer que cada vez que os poderes de root foram usados com gedit , ele registra o nome do arquivo que foi editado. Aqui está o código.

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

Conteúdo do arquivo de log gsu-log-file-for-gedit após algumas edições:

__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"
    
por WinEunuuchs2Unix 17.03.2018 / 03:42