Bagaimana saya dapat menyebabkan skrip untuk masuk ke file terpisah berapa kali dieksekusi?

11

Saya perlu menulis skrip yang menulis ke file berapa kali skrip ini telah dieksekusi.

Bagaimana saya bisa melakukan itu?

Milen Grigorov
sumber

Jawaban:

15

Saya berasumsi Anda ingin memiliki satu file countfileyang hanya berisi satu nomor tunggal yang mewakili penghitung eksekusi.

Anda dapat membaca penghitung ini ke dalam variabel shell $countermisalnya menggunakan salah satu dari baris ini:

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

Penambahan integer sederhana dapat dilakukan di Bash sendiri menggunakan $(( EXPRESSION ))sintaks. Maka cukup tulis kembali hasilnya ke countfile:

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

Anda mungkin juga harus menjaga skrip untuk kasus yang countfilebelum ada dan membuat skrip yang diinisialisasi dengan nilai 1.

Semuanya mungkin terlihat seperti ini:

#!/bin/bash
if [[ -f countfile ]] ; then
    read counter < countfile
else
    counter=0
fi
echo "$(( counter + 1 ))" > countfile
Komandan Byte
sumber
2
… Atau seperti ini:echo $(( $(cat countfile 2>/dev/null || echo 0) + 1 )) > countfile
PerlDuck
1
Bagus, yang sepertinya bekerja juga @PerlDuck. Saya takut bahwa itu mungkin membuka file untuk ditimpa sebelum dibaca, tetapi tampaknya sintaksis proses substitusi tampaknya mencegah itu.
Byte Commander
Poin yang bagus. Saya tidak begitu yakin apakah dijamin $(…)akan dieksekusi sebelum yang lain (terutama sebelum >). Tapi saya sering menggunakan $(cat countfile 2>/dev/null || echo 0)bagian ini untuk mendapatkan default yang wajar jika file tidak ada. Kita bisa menambahkan sponge:-) agar aman.
PerlDuck
2
Jawaban ini akan ditingkatkan dengan membungkus kode penghitungan itu dalam flockperintah untuk mencegah kondisi balapan. Lihat unix.stackexchange.com/a/409276
rrauenza
5

Biarkan skrip membuat file log, tambahkan misalnya satu baris di skrip Anda di akhir:

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

Dengan cara ini Anda dapat memformat cara Anda sendiri menampilkan tanggal dan waktu, tetapi jika Anda hanya ingin pergi dengan tanggal dan waktu penuh (dan HH:MM:SSmerupakan format yang dapat diterima untuk Anda), Anda juga dapat menggunakan:

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

Maka Anda bisa melakukan:

wc -l ~/script.log

Yang menghitung karakter baris baru dan memberi Anda perkiraan berapa banyak baris di dalam file log. Hingga Anda dapat melihat di dalam file log bahkan ketika itu dijalankan. Untuk menyesuaikannya dengan kebutuhan Anda, Anda dapat mengubah jalur dan nama yang digunakan untuk masuk. Saya baru saja melakukan contoh di sini yang menyimpan logfile di ~.

Jadi misalnya Anda ingin skrip menambahkan hitungan ini ke baris yang Anda tambahkan di akhir skrip Anda, Anda bisa melakukan sesuatu seperti ini di awal skrip Anda:

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"

Dan ubah baris Anda di akhir skrip ke sesuatu termasuk bahkan informasi itu:

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

Solusi ini menggunakan pendekatan yang sama dengan jawaban Byte Commander tetapi tidak bergantung pada aritmatika shell atau Bashism lainnya.

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

Pengalihan aliran

  1. menduplikasi aliran kesalahan standar (2) ke deskriptor file yang berbeda (3),
  2. ganti (2) dengan pengalihan ke /dev/null(untuk menekan pesan kesalahan di pengalihan berikutnya dari input readperintah jika file counter tidak ada yang diharapkan),
  3. kemudian duplikat aliran kesalahan standar asli (sekarang di 3) kembali ke tempatnya (2) dan
  4. tutup salinan stream kesalahan standar (3).
David Foerster
sumber
1

Pendekatan yang berbeda

File penghitung terpisah memiliki kekurangan:

  • Dibutuhkan 4096 byte (atau apa pun ukuran blok Anda) untuk setiap file penghitung.
  • Anda harus mencari nama file dalam skrip bash dan kemudian buka file untuk melihat jumlah.
  • Tidak ada penguncian file (dalam jawaban lain) sehingga ada kemungkinan bahwa dua orang memperbarui konter pada waktu yang sama (disebut kondisi lomba dalam komentar di bawah jawaban Byte Commander).

Jadi jawaban ini tidak menghilangkan file penghitung yang terpisah dan menempatkan hitungan dalam skrip bash itu sendiri!

  • Menempatkan penghitung di skrip bash itu sendiri memungkinkan Anda untuk melihat di dalam skrip Anda sendiri berapa kali skrip dijalankan.
  • Menggunakan flockjaminan bahwa untuk sesaat tidak mungkin bagi dua pengguna untuk menjalankan skrip pada saat yang sama.
  • Karena nama file counter bukan kode keras, Anda tidak perlu mengubah kode untuk skrip yang berbeda, Anda cukup sumber atau salin dan tempel dari file stub / boilerplate.

Kode

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

Pendekatan lain menggunakan File Log

Mirip dengan jawaban Videonauth, saya menulis jawaban file log di sini: Skrip Bash untuk mempertahankan jejak audit / log file yang diakses untuk login setiap kali kekuatan root digunakan dengan geditatau nautilus.

Hasil tangkapannya daripada menggunakan gksuskrip namanya gsudan memanggil pkexeccara "modern" menggunakan sudo di GUI, jadi saya diberitahu.

Keuntungan lain adalah tidak hanya mengatakan setiap kali kekuatan root digunakan dengan gedittetapi mencatat nama file yang telah diedit. Ini kodenya.

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

Isi file log gsu-log-file-for-geditsetelah beberapa pengeditan:

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