Como posso fazer com que um script efetue login em um arquivo separado, o número de vezes que foi executado?


11

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

Como eu posso fazer isso?

Respostas:


15

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

Você pode ler este contador em uma variável shell, $counterpor exemplo, usando uma destas linhas:

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

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

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

Provavelmente, você também deve proteger seu script para o caso que countfileainda não existe e criar um inicializado com o valor 1.

A coisa toda pode ficar assim:

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

2
... ou assim:echo $(( $(cat countfile 2>/dev/null || echo 0) + 1 )) > countfile
PerlDuck

1
Bom, isso parece funcionar tão bem @PerlDuck. Eu tinha medo que ele pudesse abrir o arquivo para substituição antes de ser lido, mas aparentemente a sintaxe de substituição do processo parece impedir isso.
Byte Commander

Bom ponto. Não tenho muita certeza se é garantido que $(…)seja executado antes de qualquer outra coisa (especialmente antes do >). Mas geralmente uso a $(cat countfile 2>/dev/null || echo 0)peça para obter um padrão razoável, caso o arquivo não exista. Poderíamos adicionar um sponge:-) para estar seguro.
PerlDuck

2
Essa resposta seria aprimorada envolvendo esse código de contagem em um flockcomando para evitar condições de corrida. Veja unix.stackexchange.com/a/409276
rrauenza

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 forma como apresenta a data e a hora, mas se você deseja apenas uma data e hora completas (e HH:MM:SSé um formato aceitável para 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 da nova linha e fornece uma estimativa de quantas linhas estão dentro do arquivo de log. Até isso, você pode ver dentro do arquivo de log, mesmo quando ele foi executado. Para adaptá-lo às suas necessidades, você pode alterar os caminhos e nomes usados ​​para o log. Acabei de fazer um exemplo aqui que salva o arquivo de log ~.

Portanto, por exemplo, você deseja que o script adicione essa contagem à linha que você adicionou no final do script, você pode fazer algo assim no início do 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 mude sua linha no final do script para algo que inclua até essas informações:

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

5

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

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. duplicar o fluxo de erros 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 subsequente da entrada do readcomando se o arquivo do contador estiver faltando, como esperado),
  3. depois duplique o fluxo de erro padrão original (agora às 3) de volta no lugar (2) e
  4. feche a cópia do fluxo de erro padrão (3).

1

Uma abordagem diferente

Um arquivo de contador separado tem desvantagens:

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

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

  • Colocar o contador no próprio script bash permite ver no próprio script quantas vezes ele foi executado.
  • Usando flockgarantias de que, por um breve momento, não é 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 para scripts diferentes, basta fonte-lo ou copiá-lo e colá-lo a partir de um arquivo stub / boilerplate.

O código

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

Outra abordagem usando um arquivo de log

Semelhante à resposta da Videonauth, escrevi uma resposta para o arquivo de log aqui: Script Bash para manter a trilha de auditoria / log de arquivos acessados para log toda vez que os poderes de raiz eram usados ​​com geditou nautilus.

O problema, porém, é que, em vez de usar gksuo script, é nomeado gsue invoca pkexeca maneira "moderna" de usar o sudo na GUI, pelo que me disseram.

Outra vantagem não é apenas a indicação de cada vez que os poderes raiz foram usados, geditmas também 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-geditapó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"
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.