이름이 파일 목록의 줄과 일치하지 않는 디렉터리의 모든 파일을 삭제합니다.

이름이 파일 목록의 줄과 일치하지 않는 디렉터리의 모든 파일을 삭제합니다.

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

다음 스크립트를 실행하세요.

  1. 처음에는 디렉토리에 있는 모든 파일을 찾고 출력을 다른 파일에 저장합니다 all_files.
  2. 우리는 다음과 같은 파일 목록을 포함하는 파일을 가지고 있습니다.아니요삭제( not_to_be_deleted_files).
  3. not_to_be_deleted_files두 파일이 모두 필요하므로 끝에 파일 이름을 추가했습니다 .files_to_be_deletednot_to_be_deleted_files
  4. 이제 Linux 명령을 사용하여 삭제해야 할 파일을 찾고 join출력을 files_to_be_deleted 파일로 리디렉션하고 있습니다.
  5. 이제 마지막 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먼저 디렉터리에서 시도한 다음 원래 디렉터리의 파일을 삭제하세요.

관련 정보