スクリプトを実行した回数だけ別のファイルにログインさせるにはどうすればよいですか?


回答:


15

countfile実行カウンタを表す1つの数値のみを含む1つのファイルが必要だと思います。

このカウンターをシェル変数に読み込むには、$counterたとえば次のいずれかの行を使用します。

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

単純な整数の加算は、$(( EXPRESSION ))構文を使用してBash自体で実行できます。次に、単に結果を書き戻しますcountfile

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

また、おそらくcountfileまだ存在しない場合のためにスクリプトを保護し、値1で初期化されたスクリプトを作成する必要があります。

全体は次のようになります。

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

2
…またはこのように:echo $(( $(cat countfile 2>/dev/null || echo 0) + 1 )) > countfile
PerlDuck 2017

1
@PerlDuckでも動作するようです。読み込まれる前にファイルが上書きのために開かれるのではないかと心配でしたが、どうやらプロセス置換構文がそれを妨げているようです。
バイトコマンダー

いい視点ね。$(…)それが他の何よりも前に(特にの前に>)実行されることが保証されているかどうかは、完全にはわかりません。しかし$(cat countfile 2>/dev/null || echo 0)、ファイルが存在しない場合に備えて、このパーツを使用して妥当なデフォルトを取得することがよくあります。sponge安全のために:-)を追加できます。
PerlDuck 2017

2
この回答はflock、競合状態を防ぐために、そのカウントコードをコマンドにラップすることで改善されます。unix.stackexchange.com/a/409276を
rrauenza '20

5

スクリプトにログファイルを作成させるだけです。たとえば、スクリプトの最後に次の行を追加します。

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

この方法では、日付と時刻を自分で提示する方法を自分でフォーマットできますが、完全な日付と時刻をそのまま使用したい場合(これHH:MM:SSは許容できる形式です)、次のように使用することもできます。

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

それからあなたは行うことができます:

wc -l ~/script.log

これは改行文字を数え、ログファイル内の行数の概算を示します。それまでは、実行された場合でもログファイル内で確認できます。これをニーズに合わせるために、ログに使用するパスと名前を変更できます。ここで、ログファイルをに保存する例を実行しました~

たとえば、スクリプトでこのカウントをスクリプトの最後に追加した行に追加したい場合は、スクリプトの先頭で次のようにします。

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"

そして、スクリプトの最後の行を、その情報を含むも​​のに変更します。

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

5

このソリューションは、バイト司令官の答えと同じアプローチを使用しますが、シェル演算や他のバシズムに依存していません。

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

ストリームのリダイレクト

  1. 標準エラーストリーム(2)を別のファイル記述子(3)に複製します。
  2. それを(2)へのリダイレクトに置き換えます(カウンターファイルが予期せず欠落している場合/dev/nullに、readコマンドの入力の後続のリダイレクトでエラーメッセージを抑制するため)。
  3. 後で元の標準エラーストリーム(現在は3)を複製して所定の場所(2)に戻し、
  4. 標準エラーストリームのコピーを閉じます(3)。

1

別のアプローチ

別のカウンターファイルには欠点があります。

  • 各カウンターファイルには、4096バイト(またはブロックサイズが何であれ)かかります。
  • bashスクリプトでファイルの名前を検索し、ファイルを開いてカウントを確認する必要があります。
  • (他の回答では)ファイルがロックされないため、2人が同時にカウンターを更新する可能性があります(バイトコマンダーの回答の下のコメントでは競合状態と呼ばれます)。

したがって、この答えは別のカウンターファイルを排除し、bashスクリプト自体にカウントを入れます!

  • カウンターをbashスクリプト自体に配置すると、スクリプト自体の中で何回実行されたかを確認できます。
  • 使用してflock2人のユーザーが同時にスクリプトを実行するために一瞬のためにそれが不可能だということを保証します。
  • カウンターファイル名はハードコーディングされていないため、さまざまなスクリプトのコードを変更する必要はありません。カウンターファイル名をソースするか、スタブ/ボイラープレートファイルからコピーして貼り付けることができます。

コード

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

ログファイルを使用した別のアプローチ

Videonauthの回答と同様に、ここにログファイルの回答を書きました:またはでルート権限が使用されるたびにログにアクセスするためにアクセスされたファイルの監査証跡/ログを維持するためのBashスクリプトgeditnautilus

ただし、キャッチgksuはスクリプトを使用するのではなく、名前が付けられ、GUIでsudoを使用する「モダンな」方法をgsu呼び出すpkexecため、通知されます。

別の利点は、ルートパワーが使用されるたびに表示されるだけでgeditなく、編集されたファイル名をログに記録することです。これがコードです。

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

gsu-log-file-for-geditいくつかの編集後のログファイルの内容:

__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"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.