如何使用 bash 增量重命名一批日誌檔案而不覆寫?

如何使用 bash 增量重命名一批日誌檔案而不覆寫?

我遇到了一個問題,我的解決方案通過了初始測試案例,但提交時失敗了 50%。

問題:一個目錄包含多個檔案和資料夾,其中有些檔案是不同類型的日誌; error.log、error.log.1、error.log.2、access.log.1、access.log.2 ..etc 這些檔案的內容映射到第二天,因此「cat error.log.1」有「第二天日誌」..等

任務是僅增加日誌末尾的數字,並保持目錄的其餘內容不變。另外,為每種日誌類型建立一個空文件。

例如:

./
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, 每個類型的最大 #Files < 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用於每個現有文件,一個touch在最後用於創建新文件。 (觸控可以替換為使用重定向建立檔案的循環,以將外部進程的數量減少 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[@]}"

執行此操作的順序與 @Haxiel 的解決方案生成的順序不同,這首先移動所有具有 2 位數字的文件,然後移動所有具有單位數字的文件,最後移動以“.log”結尾的文件,而不是處理所有具有相同第一部分的文件放在一起。

最初的問題說文件少於 1000 個,每個文件少於 21 個版本。它沒有說如果超過這個數字該怎麼辦。該解決方案最多可支援每個文件 100 個版本,並且只需擴展模式即可擴展到 1000 個或更多。

文件數量受到 bash 可用內存量的限制。

我相信這是一個更好的解決方案,因為它只嘗試處理存在的文件,而不是為每個名稱嘗試 N 個文件。當 N 很小(例如 21)時,這並不重要。

相關內容