한 폴더에서 다른 폴더로 20 개의 가장 오래된 파일 만 이동시키기위한 스크립트를 작성하려면 어떻게해야합니까? 폴더에서 가장 오래된 파일을 가져 오는 방법이 있습니까?
한 폴더에서 다른 폴더로 20 개의 가장 오래된 파일 만 이동시키기위한 스크립트를 작성하려면 어떻게해야합니까? 폴더에서 가장 오래된 파일을 가져 오는 방법이 있습니까?
답변:
의 출력을 구문 분석 ls
입니다 신뢰할 수 없습니다 .
대신 find
파일을 찾아 sort
타임 스탬프별로 주문하십시오. 예를 들면 다음과 같습니다.
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
먼저, find
명령은 현재 디렉토리 ( .
) 에서 모든 파일과 디렉토리를 찾고 현재 디렉토리 ( )의 서브 디렉토리에서는 찾지 못합니다 -maxdepth 1
.
타임 스탬프가 중요합니다. %T@
에 대한 형식 지정자 -printf
로 분해 T
파일 (mtime에)과의 "마지막 수정 시간"표시, @
소수 초를 포함하여 "1970 년 이후 초"를 나타냅니다.
공간은 단지 임의의 구분자입니다. 파일의 전체 경로는 나중에 참조 할 수 있도록하며, NULL 문자는 파일 이름에 잘못된 문자이므로 종료 문자는 경로 끝에 도달했음을 알려줍니다. 파일.
내가 포함 한 2>/dev/null
사용자가 액세스 권한이없는 파일이 제외되어 있지만, 그들에 대한 오류 메시지가 억제되어 제외되고 너무.
의 결과 find
명령 는 현재 디렉토리의 모든 디렉토리 목록입니다. 이 목록은 다음 sort
과 같이 지시됩니다.
-z
개행 문자 대신 널 종료 문자로 NULL을 처리하십시오.-n
숫자로 정렬seconds-since-1970이 항상 올라가므로 타임 스탬프가 가장 작은 파일을 원합니다. 첫 번째 결과sort
는 가장 작은 숫자의 타임 스탬프를 포함하는 줄입니다. 남아있는 것은 파일 이름을 추출하는 것입니다.
의 결과 find
, sort
파이프 라인을 통해 전달되는 프로세스 대체 에 while
이 표준 입력에서 파일을 인 것처럼 읽을된다. while
차례로 호출read
입력을 처리하기 위해 합니다.
문맥에서 read
우리는 IFS
변수를 none으로 설정했다 . 이것은 공백이 구분자로 부적절하게 해석되지 않음을 의미한다. read
는 -r
이스케이프 확장을 비활성화 -d $'\0'
하고 find
, sort
파이프 라인 의 출력과 일치하여 줄 끝 구분 기호를 NULL로 만듭니다.
타임 스탬프와 공백이 오는 가장 오래된 파일 경로를 나타내는 첫 번째 데이터 청크가 변수로 읽 힙니다 line
. 다음으로 매개 변수 대체 는 expression과 함께 사용되며 #*
, 이는 문자열의 시작부터 공백을 포함한 첫 번째 공백까지 모든 문자를 아무 것도없이 대체합니다. 수정 타임 스탬프를 제거하고 파일의 전체 경로 만 남겨 둡니다.
이 시점에서 파일 이름이 저장되며 $file
원하는대로 작업 할 수 있습니다. 당신이 함께 일을 완료 할 때 문 의지 루프와$file
while
read
명령은 다음 청크와 다음 파일 이름을 추출, 다시 실행됩니다.
아니요. 간단한 방법은 버그가 있습니다.
를 사용 ls -t
하여 head
또는 tail
(또는 무엇이든 ) 파이프 하면 파일 이름에 줄 바꿈이있는 파일이 손상됩니다. 이 경우 mv $(anything)
다음 이름에 공백이있는 파일 파손의 원인이됩니다. 이 경우 mv "$(anything)"
다음에 파일 이름으로 줄 바꿈을 후행하는 것은 파손의 원인이됩니다. 당신이 경우 read
없이 -d $'\0'
당신은 이름에 공백을 가진 파일에 휴식 것입니다.
아마도 특정한 경우에는 더 간단한 방법으로 충분하다는 것을 알고 있지만, 그렇게하지 않을 경우 스크립트에 이와 같은 가정을 쓰면 안됩니다.
#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
다음과 같이 전화하십시오 :
move-oldest /mnt/backup/ /var/log/foo/ 20
가장 오래된 20 개의 파일을에서 /var/log/foo/
로 이동합니다 /mnt/backup/
.
파일 과 디렉토리를 포함하고 있습니다 . 파일의 경우에만 호출에 추가 -type f
하십시오 find
.
이 답변의 개선을위한 enzotib 와 Павел Танков 에게 감사합니다 .
-n
. 적어도 내 버전에서는 십진수를 올바르게 정렬하지 않습니다. 날짜에서 점을 제거하거나 -printf '%TY-%Tm-%TdT%TH:%TM:%TS %p\0' | sort -rz
, ISO 날짜 등을 사용해야 합니다.
.
당신과 관련 이 없어야합니다) sort -z -n -t. -k1
.
%TS
를 나타냅니다 . 또한 "분수 부분"형식 00.0000000000
으로 표시되므로 세분성을 잃게됩니다. 최근 GNU 는 "버전 정렬" sort
을 사용하여이 문제를 해결할 수있었습니다.이 -V
버전은 예상대로 이러한 유형의 부동 소수점을 처리합니다.
%T@
은 0으로 채워지기 때문에 작동합니다.
zsh에서 가장 쉽습니다. Om
glob 한정자 를 사용하여 날짜 (가장 오래된 것부터)로 일치를 정렬하고 [1,20]
한정자를 사용하여 처음 20 개의 일치 만 유지할 수 있습니다.
mv -- *(Om[1,20]) target/
D
도트 파일도 포함 하려면 한정자를 추가하십시오 . 더하다.
디렉토리가 아닌 일반 파일 만 일치 시키려면 .
zsh가없는 경우 Perl one-liner가 있습니다 (80 자 미만으로 할 수 있지만 명확성을 위해 추가 비용으로).
perl -e '@files = sort {-M $b <=> -M $a} glob("*"); foreach (@files[0..1]) {rename $_, "target/$_" or die "$_: $!"}'
POSIX 도구 또는 bash 또는 ksh 만 사용하면 날짜별로 파일을 정렬하는 것이 쉽지 않습니다. 을 사용하여 쉽게 할 수 ls
있지만 출력을 구문 분석하는 데 ls
문제가 있으므로 파일 이름에 줄 바꿈 이외의 인쇄 가능한 문자 만 포함 된 경우에만 작동합니다.
ls -tr | head -n 20 | while IFS= read -r file; do mv -- "$file" target/; done
ls -t
출력을 tail
또는로 결합하십시오 head
.
모든 파일 이름에 공백 및 \[*?
: 이외의 인쇄 가능한 문자 만 포함 된 경우에만 작동하는 간단한 예 :
mv $(ls -1tr | head -20) other_folder
ls -1Atr
touch $'foo\n*'
. 해당 파일이 저장된 상태에서 mv "$ (ls)"를 실행하면 어떻게됩니까?
이것을 위해 GNU find를 사용할 수 있습니다 :
find -maxdepth 1 -type f -printf '%T@ %p\n' \
| sort -k1,1 -g | head -20 | sed 's/^[0-9.]\+ //' \
| xargs echo mv -t dest_dir
find가 수정 시간 (1970 년에서 초 단위)과 현재 디렉토리의 각 파일 이름을 인쇄하는 경우, 출력은 첫 번째 필드에 따라 정렬되고 가장 오래된 20 개가 필터링되어로 이동됩니다 dest_dir
. echo
명령 행을 테스트 한 경우를 제거하십시오 .
아무도 파일 이름에 포함 된 줄 바꿈 문자 (임베디드 포함)를 제공하는 bash 예제를 게시하지 않았으므로 여기에 하나가 있습니다. 가장 오래된 (mdate) 정규 파일 3 개를 이동합니다.
move=3
find . -maxdepth 1 -type f -name '*' \
-printf "%T@\t%p\0" |sort -znk1 | {
while IFS= read -d $'\0' -r file; do
printf "%s\0" "${file#*$'\t'}"
((--move==0)) && break
done } |xargs -0 mv -t dest
이것은 테스트 데이터 스 니펫입니다
# make test files with names containing \n, \t and " "
rm -f '('?[1-4]' |?)'
for f in $'(\n'{1..4}$' |\t)' ;do sleep .1; echo >"$f" ;done
touch -d "1970-01-01" $'(\n4 |\t)'
ls -ltr '('?[1-4]' |'?')'; echo
mkdir -p dest
다음은 검사 결과 스 니펫입니다.
ls -ltr '('?[1-4]' |'?')'
ls -ltr dest/*
GNU를 사용하는 것이 가장 쉽습니다 find
. 매일 Linux DVR에서 사용하여 하루보다 오래된 비디오 감시 시스템의 녹화물을 삭제합니다.
구문은 다음과 같습니다.
find /path/to/files/* -mtime +number_of_days -exec mv {} /path/to/folder \;
그 기억 find
을 정의를 실행의 24 시간이 아니라 하루. 따라서 오후 11시에 마지막으로 수정 된 파일은 오전 1시에 삭제되지 않습니다.
find
와 결합 할 수도 cron
있으므로 루트로 다음 명령을 실행하여 삭제 일정을 자동으로 예약 할 수 있습니다.
crontab -e << EOF
@daily /usr/bin/find /path/to/files/* -mtime +number_of_days -exec mv {} /path/to/folder \;
EOF
find
매뉴얼 페이지를 참조하여 언제든지 자세한 정보를 얻을 수 있습니다 .
man find
다른 답변이 내 질문과 목적에 맞지 않기 때문에이 쉘은 CentOS 7에서 테스트되었습니다.
oldestDir=$(find /yourPath/* -maxdepth 0 -type d -printf '%T+ %p\n' | sort | head -n 1 | tr -s ' ' | cut -d ' ' -f 2)
echo "$oldestDir"
rm -rf "$oldestDir"