1000개가 넘는 파일이 있는 디렉토리가 있습니다. 텍스트 파일에는 한 줄에 하나씩 약 50개의 파일 이름이 있습니다. 파일 이름이 목록의 항목과 일치하지 않는 디렉터리의 모든 파일을 삭제하고 싶습니다. 가장 좋은 접근 방식은 무엇입니까? 쉘 스크립트를 시작했지만 파일 이름이 목록에 있는지 확인하는 올바른 명령을 찾을 수 없습니다. 감사해요.
답변1
파일을 삭제하는 방법을 묻는 질문은 매우 주의해서 수행해야 한다는 것을 알고 있습니다. 내 첫 번째 대답은 너무 성급했고 파일 목록이 egrep과 함께 사용하기에는 잘못된 형식일 수 있다는 점을 고려하지 않았습니다. 이 위험을 줄이기 위해 답변을 편집했습니다.
이는 이름에 공백이 없는 파일에 대해 작동합니다.
먼저 파일 목록을 다시 작성하여 정확한 파일 이름과 일치하는지 확인하십시오.
sed -e 's,^,^,' -e 's,$,$,' filelist > newfilelist
rm 명령 빌드
cd your_directory
ls | egrep -vf newfilelist | xargs -n 1 echo rm > rmscript
rm 스크립트가 작동하는지 확인하세요("vim" 또는 "less"를 사용하여 수행할 수 있음).
그런 다음 다음을 수행하십시오.
sh -x rmscript
파일 이름에 공백이 포함된 경우( "
파일 이름에 공백이 포함되어 있으면 작동하지 않음):
ls | egrep -vf newfilelist | sed 's,^\(.*\)$,rm "\1",' > rmscript
물론 파일 목록이 같은 디렉터리에 있으면 안 됩니다!
편집하다:
Nathan의 파일 목록에는 디렉터리의 모든 파일과 일치하는 이름이 포함되어 있습니다(예: "html"은 "bob.html"과 일치함). 따라서 egrep -vf
모든 스트림이 흡수되므로 아무것도 삭제되지 않습니다. 각 파일 이름 주위에 "^"와 "$"를 넣는 명령을 추가했습니다. 운 좋게도 Nathan의 파일 목록이 정확했습니다. CR-LF 줄 종료 또는 추가 공백을 사용하는 DOS 형식의 경우 egrep은 파일을 유지하지 않으며 모든 파일이 삭제됩니다.
답변2
다음 매개변수를 사전 구성합니다 find
.
{
read -r
keep=( -name "$REPLY" ) # no `-o` before the first one.
while read -r; do
keep+=( -o -name "$REPLY" )
done
} < file_list.txt
find . -type f ! \( "${keep[@]}" \) -exec echo rm {} +
이 echo
부품을 사용하여 무엇을 만들 수 있는지 확인하세요. 부품만 제거 echo
하면 바로 사용할 수 있습니다.
업데이트: 데모:
##
# Demonstrate what files exist for testing.
# Show their whitespace:
~/foo $ printf '"%s"\n' *
" op"
" qr"
"abc"
"def"
"gh "
"ij "
"k l"
"keep"
"m n"
##
# Show the contents of the "keep" file,
# Including its whitespace:
~/foo $ cat -e keep
keep$
abc$
gh $
k l$
op$
##
# Execute the script:
~/foo $ { read -r; keep=( -name "$REPLY" ); while read -r ; do keep+=( -o -name "$REPLY" ); done } < keep
~/foo $ find . -type f ! \( "${keep[@]}" \) -exec rm {} +
##
# Show what files remain:
~/foo $ printf '"%s"\n' *
" op"
"abc"
"gh "
"k l"
"keep"
답변3
그리고 zsh
:
mylist=(${(f)"$(<filelist)"})
print -rl -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)
filelist
배열의 행을 읽은 다음 다음을 사용합니다.전역 한정자/ e
문자열전역/선택 배열에 없는 파일 이름만: .
일반 파일만 선택하고( D
목록에 도트 파일이 포함된 경우 추가됨) ^e_'expression'_
추가로 표현식이 false를 반환하는 선택 파일만 무효화합니다. 즉, 해당 이름( $REPLY
)배열의 요소가 아닙니다..
결과가 만족스러우면 파일을 실제로 삭제하려면 다음 print -rl
과 같이 바꾸십시오.rm
rm -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)
파일을 재귀적으로 선택하고 삭제하려면 */**
glob을 사용하십시오.${REPLY:t}
전역 수정자:
rm -- */**(.^e_'(($mylist[(Ie)${REPLY:t}]))'_)
답변4
다음 스크립트를 실행하세요.
- 처음에는 디렉토리에 있는 모든 파일을 찾고 출력을 다른 파일에 저장합니다
all_files
. - 우리는 다음과 같은 파일 목록을 포함하는 파일을 가지고 있습니다.아니요삭제(
not_to_be_deleted_files
). not_to_be_deleted_files
두 파일이 모두 필요하므로 끝에 파일 이름을 추가했습니다 .files_to_be_deleted
not_to_be_deleted_files
- 이제 Linux 명령을 사용하여 삭제해야 할 파일을 찾고
join
출력을files_to_be_deleted
파일로 리디렉션하고 있습니다. - 이제 마지막 while 루프에서는 그 안의 모든 파일 이름을 읽고
files_to_be_deleted
해당 파일 이름에 언급된 파일을 삭제합니다.
스크립트는 다음과 같습니다.
find /home/username/directory -type f | sed 's/.*\///' > all_files
echo all_files >> not_to_be_deleted_files
echo not_to_be_deleted_files >> not_to_be_deleted_files
echo files_to_be_deleted >> not_to_be_deleted_files
join -v 1 <(sort all_files_listed) <(sort files_not_to_be_deleted) > files_to_be_deleted
while read file
rm "$file"
done < files_to_be_deleted
폴리스티렌: 아마도, 스크립트로 저장해서 실행하고 싶다면 를 이용하여 스크립트 이름을 추가할 수도 있습니다 echo scriptname >> not_to_be_deleted_files
.
필수는 아니지만 나중에 후회할 일이 없기 때문에 그렇게 하는 것을 선호합니다. 작은 파일 세트로 테스트했는데 내 시스템에서 작동합니다. 그러나 확실하게 확인하려면 test
먼저 디렉터리에서 시도한 다음 원래 디렉터리의 파일을 삭제하세요.