私のソリューションは初期テストケースに合格しましたが、送信時に50%の時間に失敗する問題が発生しました。
問題:ディレクトリには複数のファイルとフォルダが含まれており、その中にはerror.log、error.log.1、error.log.2、access.log.1、access.log.2などのさまざまな種類があります。ログです。 。このファイルの内容は翌日にマッピングされるため、「cat error.log.1」には「Next Day Log」..etcがあります。
タスクは、ログの末尾にある数字だけを増やし、ディレクトリの残りの部分は変更せずに残すことです。また、ログタイプごとに空のファイルを作成します。
たとえば、
./
example_dir
example2_dir
error.log
error.log.1
info.log.20
access.log.1
readme.txt
スクリプトはディレクトリを次に変更します。
./
example_dir (unchanged)
example2_dir (unchanged)
error.log (empty)
error.log.1 (originally error.log)
error.log.2 (originally error.log.1)
info.log (empty)
info.log.21 (originally info.log.20)
access.log (empty)
access.log.2 (originally access.log.1)
readme.txt (unchanged)
条件:#ディレクトリのファイル< 1000、各タイプの最大#ファイル< 21
私の解決策:
#!/bin/bash
declare -a filenames
# Renaming in ascending order will lead to overwrite; so start rename from the bottom
files=$(find . -maxdepth 1 -name "*.log.*" -exec basename {} \; | sort -rn)
for i in $files; do
currentFileNumber=$(echo -e "$i" | sed -e 's/[^0-9]*//g') # Extract the current number from the filename
fileName=$(echo -e "$i" | sed -e 's/\.[0-9]*$//g') # Extract the name without the trailing number
newFileNumber=$(("$currentFileNumber" + 1)) # Increment the current number
mv "$i" "$fileName.$newFileNumber" # Rename and append the incremented value
if [[ ! ${filenames[*]} =~ ${fileName} ]] # Store names of existing types to create empty files
then
filenames=("${filenames[@]}" "${fileName}")
fi
# Could make use of [[ -e "$fileName.log" ]] instead of an array, but won't pass the test for some reason
done
for j in "${filenames[@]}"; do touch "$j"; done # Create the empty files
unset filenames
失敗したテストケースが表示されないため、この問題をよりよく解決する方法がわかりません。
答え1
これは楽しい練習だったし、ここに私の解決策があります。
#/bin/bash
log_names=$(for logfile in $(find . -type f -name '*.log*'); do echo ${logfile%.[0-9]*}; done | sort -u)
for name in $log_names; do
echo "Processing $name"
i=20
until [[ "$i" -eq 0 ]]; do
if [[ -f "$name.$i" ]]; then
next_num=$((i+1))
mv -v "$name.$i" "$name.$next_num"
fi
i=$((i-1))
done
if [[ -f "$name" ]]; then
mv -v "$name" "$name.1"
fi
touch "$name"
done
log_names変数は、find
コマンドを使用してログファイルのリストを取得します。次に、文字列置換を適用して数値サフィックスを削除します。次に、重複した項目をソートして削除します。
この時点で、次のディレクトリに一意のログファイル名のリストが表示されます./access.log ./error.log ./info.log
。
その後、ループを使用して各名前を順番に処理しますfor
。
これで、各ファイルの最大数は20個と聞きました。ここから始めて、until
ループを使ってカウントダウンします。
ロジックはmv
簡単です。 「filname.number」がある場合は、「filename.(number+1)」に移動します。
ループが完了するとuntil
(i = 0)、数字のサフィックスを持たない非回転ファイルが残ることがあります。その場合は、filename.1にアクセスしてください。
最後のステップは空のファイルを作成することですtouch
。
実行例:
$ ls
access.log.1 error.log error.log.1 example_dir example2_dir info.log.20 readme.txt rotate.bash
$ bash rotate.bash
Processing ./access.log
'./access.log.1' -> './access.log.2'
Processing ./error.log
'./error.log.1' -> './error.log.2'
'./error.log' -> './error.log.1'
Processing ./info.log
'./info.log.20' -> './info.log.21'
$ ls -1
access.log
access.log.2
error.log
error.log.1
error.log.2
example_dir
example2_dir
info.log
info.log.21
readme.txt
rotate.bash
答え2
@Haxielがこのソリューションを投稿しました。これは私が「最も簡単だ」と思うのと似ています。ループfor
の代わりにループを使用しますuntil
。
mv
これは、既存のファイルごとに1つずつ、touch
最後に新しいファイルを作成するために1つずつ、ほぼ最小限の外部プロセスを使用することです。 (タッチはリダイレクトを使用してファイルを生成するループに置き換えられ、外部プロセスの数を1減らすことができます。)
#!/bin/bash
shopt -s nullglob # Reduce the number of things we have to work with
# get a list of the files we want to work with.
files=( *.log *.log.[1-9] *.log.[1-9][0-9] )
# reverse the list into rfiles, getting rid of non-file things
rfiles=()
for ((i=${#files[@]}-1;i>=0;i--)) ; do
if [ -f "${files[i]}" ] ; then
rfiles+=("${files[i]}")
fi
done
# exit early if there is nothing to do
if [ ${#rfiles[@]} -eq 0 ] ; then
exit 0
fi
# an array of the files we need to create
typeset -A newfiles
# Loop over the reversed file list
for f in "${rfiles[@]}"; do
# Get everything up to the last "log"
baseName=${f%log*}log
# Remove up to the last "log" and then the optional "."
currentFileNum=${f#"$baseName"}
currentFileNum=${currentFileNum#.}
mv -v "$f" "$baseName.$((currentFileNum+1))"
# record the name to make the new files
newfiles[$baseName]=1
done
# Create all the needed new files, using the names stored in the array
touch "${!newfiles[@]}"
これを行う順序は、すべてのファイルを処理するのではなく、2桁の数字を持つすべてのファイルを最初に移動し、次に1桁の数字を持つすべてのファイルを移動し、最後に「.log」で終わるファイルを移動する@Haxielのソリューションで生成された順序とは異なります。最初の部分が同じファイルが一緒に配置されます。
元の質問は、ファイルが1000未満であり、各ファイルのバージョンが21未満であることを示していました。この数を超えるとどうするかはわかりません。このソリューションはファイルごとに最大100のバージョンをサポートし、拡張モードのみを使用して1000以上に拡張できます。
ファイル数は、bash で使用可能なメモリー量によって制限されます。
名前ごとにN個のファイルを試すのではなく、存在するファイルだけを処理しようとするので、これがより良い解決策だと思います。 Nが小さい場合(例:21)、これは問題ではありません。