저는 Linux 스크립팅에 익숙하지 않으며 이번이 처음으로 Linux 스크립팅을 사용하므로 다음과 같은 문제가 발생합니다.
암호:
while [ $pct -gt 80 ]; do
flag=1;
ls -tr | while read file; do
if [[ $file =~ .+\.log[0-9]+ ]]; then
printf "File deleted:\n";
stat $file;
rm -r $file;
flag=1;
break;
else
flag=0;
fi;
done;
if [ $flag -eq 0]; then
break;
fi;
pct= # get the new pct;
done;
작업은 위 정규식으로 캡처된 특정 로그 파일을 가장 오래된 파일부터 삭제하는 것이므로 ls -tr을 사용합니다. while 루프를 사용하여 파일 목록을 반복하고 파일이 주어진 정규식과 일치하면 삭제합니다. 삭제할 때마다 사용 중인 애플리케이션 파일 시스템의 비율을 확인하고, 그 비율이 80%보다 큰 경우(외부 while 루프 조건에 표시됨) 프로세스를 반복합니다.
이제 파일을 삭제한 후에도 주어진 정규식 패턴의 파일이 남아 있지 않더라도 사용률이 80% 이하로 떨어지지 않으며 동일한 폴더에 남아 있는 다른 파일을 삭제할 수 없습니다. 따라서 무한루프에 빠지게 되므로, 이 경우에는 무한루프를 깨기 위해 플래그 변수를 사용하려고 했습니다. 그런데 파일을 반복하기 위해 파이프를 사용하고 있기 때문에 서브쉘이나 서브프로세스가 되어 부모 프로세스의 flags 변수를 사용할 수 없게 됩니다(서브쉘에 있는 플래그의 업데이트된 값은 부모 쉘에 반영되지 않습니다) ) (이것이 내가 (파이프에 대한 이 기사에서 읽은 것처럼)에서 수행하는 작업이므로 무한 루프가 절대 중단되지 않습니다. 누구든지 이 문제에 대한 해결책이나 대체 논리를 제안할 수 있습니까?
답변1
zsh
대신 사용하십시오 bash
:
while
pct= # get the new pct
(( pct > 80 )) &&
oldest_log_file=( *.log<->(N.Om[1]) ) &&
(( $#oldest_log_file ))
do
print -r Removing $oldest_log_file
rm -f -- $oldest_log_file
done
또는:
log_files_from_oldest_to_newest=( *.log<->(N.Om) )
while
pct= # get the new pct
(( pct > 80 )) &&
(( $#log_files_from_oldest_to_newest ))
do
print -r Removing $log_files_from_oldest_to_newest[1]
rm -f -- $log_files_from_oldest_to_newest[1]
shift 1 log_files_from_oldest_to_newest
done
또는:
zmodload zsh/stat
for log_file in *.log<->(N.Om); do
pct= # get the new pct
(( pct > 80 )) || break
stat -F %FT%T%z -LH s -- $log_file || continue
print -r Removing $log_file of size $s[size] last modified on $s[mtime]
rm -f -- $log_file
done
답변2
ls 구문 분석은 매우 취약하며 실제로는 좋은 생각이 아닙니다. 그러나 파일 이름에 공백, 탭, 줄 바꿈(수정되지 않은 것으로 가정 $IFS
)이나 전역 패턴 연산자(적어도 *
, )가 포함되지 않고 and로 시작되지 않으며 유형도 포함되지 않을 것이라고 확신하는 경우?
[
-
목차, 다음과 같이 할 수 있습니다.
#!/bin/bash
pct= # get the current pct
## are there any matching files?
files=( $(ls -tr *.log[0-9]* 2>/dev/null) );
## While pct is above the threshold and there is at least
## one file in the files array
while [[ $pct -gt 80 && ${#files[@]} -gt 0 ]]; do
printf "Deleting file:\n%s\n" "$(stat -- "${files[0]}")"
rm -- "${files[0]}"
## repopulate the files array
files=( $(ls -tr *.log[0-9]* 2>/dev/null) );
pct=85 # get the new pct;
done
여기서의 아이디어는 파일 이름을 배열에 저장하고 삭제할 때마다 배열을 다시 정의하는 것입니다. 그런 다음 "남은 파일이 있습니까?"와 "$pct가 80 미만입니까?"라는 두 가지 조건에서 루프를 실행하므로 두 조건 중 하나가 더 이상 참이 아닐 때 루프가 중지됩니다.
참고 1: 이는 와 같은 이름의 파일이 없다고 가정합니다 . 즉 , 문자열 뒤에 숫자가 포함된 모든 파일을 foo.log12bar
보고 싶고 첫 번째 숫자 뒤에 숫자가 아닌 문자가 포함된 파일 이름을 피하고 싶지 않다고 가정합니다..log
노트 2: 처음에 언급했듯이 일반적이지 않은 이름을 가진 파일 이름에는 실패합니다. 구문 분석된 출력이 ls
거의 항상 나쁜 생각인 이유는 여기를 참조하십시오 .
답변3
다음과 같이 시도해 보세요.
#!/bin/bash
dir="/path/to/dir"
# read the matching files into array "$files"
mapfile -d '' files < <(find "$dir" -maxdepth 1 -type f -regex '.*\.log[0-9]+' -print0)
for f in "${files[@]}"; do
# get the current percentage used of the filesystem
pct=$(df --output=pcent "$dir" | awk -F'[ %]' 'NR==2 {print $2}')
[ "$pct" -lt 80 ] && break
rm "$f"
done
파일 시스템의 현재 사용률이 80% 미만으로 떨어질 때까지 일치하는 파일을 한 번에 하나씩 계속 삭제합니다.또는일치하는 파일 이름이 더 이상 없을 때까지(둘 중 먼저 발생하는 날짜 기준)
여기에는 GNU df
옵션이 필요합니다 --output
.
GNU가 아닌 경우 df
다음이 작동합니다.만약에장치 이름(파일 시스템 열)에는 공백이나 %
문자가 포함되지 않습니다.
pct=$(df "$dir" | awk -F'[[:space:]]+|%' 'NR==2 {print $5}')
마운트 지점("마운트 위치" 열)에 공백이 포함되어 있지만 문자가 포함되어 있지 않은 경우 %
다음과 같은 방법을 사용할 수 있습니다.
pct=$(df "$dir" | awk '
NR==2 {
for (i=NF; i > 0; i--) { if ($i ~ /%/) {break} };
sub("%","",$i);
print $i
}')
(예, df
출력에서 데이터를 추출하는 것은 거의 같은 이유로 처음 생각보다 어렵습니다.ls를 구문 분석하는 것은 나쁜 생각입니다.)
타임스탬프별로 파일 이름을 정렬해야 하는 경우(가장 오래된 파일을 먼저 삭제할 수 있도록) 내 답변에 설명된 방법을 사용할 수 있습니다.가장 오래된 파일을 한 디렉토리에서 다른 디렉토리로 이동하는 쉘 스크립트. 다음과 같이 보일 것입니다:
mapfile -d '' files < <(
find "$dir" -maxdepth 1 \
-type f \
-regex '.*\.log[0-9]+' \
-printf '%C@\t%p\0' |
sort -z -k1,1 -r -n |
cut -z -f2
)
find
이를 위해서는 , sort
및 의 GNU 버전도 필요합니다 cut
.
답변4
다른 훌륭한 답변에서 알 수 있듯이, 원하는 것을 달성하는 방법은 다양하므로 다른 구현을 포기하지 않겠습니다.
지금은 ls
출력 처리의 함정을 무시하세요.
문제의 핵심은 변수 값을 루프 외부에 유지하는 방법인 것 같습니다. 그래서 나는 이것이 당신이 찾고 있는 것이라고 생각합니다:
flag=foo
while read file; do
echo "do stuff with $file"
flag=bar
done < <(ls -tr) # preferably do something else to get your list
echo "$flag"