내 솔루션이 초기 테스트 사례를 통과했지만 제출 시 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
이는 기존 파일마다 하나씩, 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[@]}"
이 작업을 수행하는 순서는 모든 파일을 처리하는 대신 2자리 숫자가 있는 모든 파일을 먼저 이동한 다음 한 자리 숫자가 있는 모든 파일을 이동하고 마지막으로 ".log"로 끝나는 파일을 이동하는 @Haxiel의 솔루션에서 생성된 순서와 다릅니다. 첫 번째 부분이 동일한 파일이 함께 배치됩니다.
원래 질문에는 파일이 1000개 미만이고 각 파일의 버전이 21개 미만이라고 나와 있었습니다. 이 숫자를 초과하면 어떻게 해야 하는지는 나와 있지 않습니다. 이 솔루션은 파일당 최대 100개 버전을 지원하며 확장 모드만 사용하면 1000개 이상으로 확장할 수 있습니다.
파일 수는 bash에 사용 가능한 메모리 양에 따라 제한됩니다.
각 이름에 대해 N개의 파일을 시도하는 대신 존재하는 파일만 처리하려고 시도하므로 이것이 더 나은 솔루션이라고 생각합니다. N이 작은 경우(예: 21) 이는 중요하지 않습니다.