목록에서 누락 된 파일을 어떻게 찾습니까?


9

파일 시스템에 있는지 확인하려는 파일 목록이 있습니다. 나는 이것을 다음 find과 같이 사용한다고 생각 했다.

for f in $(cat file_list); do
find . -name $f > /dev/null || print $f
done

(을 사용하여 zsh) 파일을 찾을 지 여부 find를 끝내는 것처럼 작동하지 않습니다 0. 나는 find출력을 생성 하는지 확인하기 위해 다른 테스트를 통과 할 수 있다고 생각 합니다 (조잡하지만 효과적인 것으로 대체하는 것 > /dev/null입니다 |grep ''). ).

find나에게 유용한 이탈 가치를 제공 하도록 강요 할 수있는 방법이 있습니까? 아니면 적어도 찾지 못한 파일의 목록을 얻으려면 ? (논리적 결합을 교묘하게 선택함으로써 후자가 더 쉬울 것이라고 생각할 수 있지만, 알아낼 때 항상 매듭에 묶여있는 것처럼 보입니다.)

배경 / 동기 부여 : "마스터"백업이 있고 로컬 시스템의 일부 파일을 삭제하기 전에 마스터 백업에 존재하는지 확인하고 싶습니다 (약간의 공간 확보). 그래서 파일 목록 ssh을 작성하여 마스터 시스템으로 가져간 다음 누락 된 파일을 찾는 가장 좋은 방법을 찾지 못한 것입니다.


훨씬 빨리 사용하도록 솔루션을 업데이트했습니다 locate.
사용자가 알 수 없음

@userunknown locate은 파일 시스템의 현재 상태를 표시하지 않으며 하루 또는 일주일이 걸릴 수 있습니다. 백업 테스트를위한 기반으로 적합합니다.
Volker Siegel

답변:


5

find특별한 성공 사례를 찾지 않는 것으로 간주합니다 (오류가 발생하지 않음). 파일이 일부 find기준 과 일치하는지 테스트하는 일반적인 방법 은 출력 find이 비어 있는지 테스트하는 것 입니다. 일치하는 파일이있을 때 효율성 -quit을 높이려면 GNU find에서 파일을 처음 일치시킬 때 종료하거나 다른 시스템 에서는 head( head -c 1가능한 경우 head -n 1표준 인 경우) 종료하여 긴 출력을 생성하지 않고 끊어진 파이프로 죽게하십시오.

while IFS= read -r name; do
  [ -n "$(find . -name "$name" -print | head -n 1)" ] || printf '%s\n' "$name"
done <file_list

bash ≥4 또는 zsh에서는 find간단한 이름 일치를 위해 외부 명령이 필요하지 않습니다 .을 사용할 수 있습니다 **/$name. 배쉬 버전 :

shopt -s nullglob
while IFS= read -r name; do
  set -- **/"$name"
  [ $# -ge 1 ] || printf '%s\n' "$name"
done <file_list

비슷한 원리의 Zsh 버전 :

while IFS= read -r name; do
  set -- **/"$name"(N)
  [ $# -ge 1 ] || print -- "$name"
done <file_list

또는 패턴과 일치하는 파일의 존재를 테스트하는 더 짧지 만 더 비밀스러운 방법이 있습니다. glob 한정자 N는 일치하는 항목이 없으면 출력을 비우고 [1]첫 번째 일치 항목 만 유지 하며 일치하는 파일 이름 대신 e:REPLY=true:확장되도록 각 일치 항목을 변경합니다 1. 따라서 일치하는 항목이 있거나 **/"$name"(Ne:REPLY=true:[1]) false확장 된 항목이 true false없는 경우로 확장됩니다 false.

while IFS= read -r name; do
  **/"$name"(Ne:REPLY=true:[1]) false || print -- "$name"
done <file_list

모든 이름을 하나의 검색으로 결합하는 것이 더 효율적입니다. 명령 행에서 시스템 길이 제한에 비해 패턴 수가 너무 많지 않은 경우 모든 이름을로 결합 -o하고 단일 find호출을 수행 한 후 출력을 후 처리 할 수 ​​있습니다. 셸 메타 문자가 포함 된 이름이없는 경우 (이름도 find패턴 임) awk를 사용하여 사후 처리하는 방법은 다음과 같습니다 (예상되지 않음).

set -o noglob; IFS='
'
set -- $(<file_list sed -e '2,$s/^/-o\
/')
set +o noglob; unset IFS
find . \( "$@" \) -print | awk -F/ '
    BEGIN {while (getline <"file_list") {found[$0]=0}}
    wanted[$0]==0 {found[$0]=1}
    END {for (f in found) {if (found[f]==0) {print f}}}
'

또 다른 방법은 Perl 및을 사용 File::Find하는 것입니다. 이렇게하면 디렉토리의 모든 파일에 대해 Perl 코드를 쉽게 실행할 수 있습니다.

perl -MFile::Find -l -e '
    %missing = map {chomp; $_, 1} <STDIN>;
    find(sub {delete $missing{$_}}, ".");
    print foreach sort keys %missing'

다른 방법은 양쪽에 파일 이름 목록을 생성하고 텍스트 비교 작업을하는 것입니다. Zsh 버전 :

comm -23 <(<file_list sort) <(print -rl -- **/*(:t) | sort)

나는 두 가지 이유로 이것을 받아들이고 있습니다. 나는 구문이 있는 zsh솔루션을 좋아한다 **. 그것은 매우 간단한 해결책이며 기계 측면에서 가장 효율적이지 않을 수도 있지만 실제로 그것을 기억하는 측면에서 가장 효율적일 것입니다! 또한 첫 번째 해결책 은 종료 코드가 "일치했습니다"와 "일치하지 않았습니다"를 구별하는 것으로 바뀌는 실제 질문 에 대한 대답 find입니다.
Andrew Stacey

9

stat파일이 파일 시스템에 존재하는지 판별하는 데 사용할 수 있습니다 .

내장 쉘 함수 를 사용하여 파일이 존재하는지 테스트해야합니다.

while read f; do
   test -f "$f" || echo $f
done < file_list

"test"는 선택 사항이며 스크립트는 실제로 스크립트없이 작동하지만 가독성을 위해 그대로 두었습니다.

편집 : 실제로 경로가없는 파일 이름 목록을 사용할 수있는 옵션이 없다면 find로 파일 목록을 한 번 만든 다음 grep을 사용하여 파일 목록을 반복하여 어떤 파일이 있는지 확인하십시오.

find -type f /dst > $TMPFILE
while read f; do
    grep -q "/$f$" $TIMPFILE || echo $f
done < file_list

참고 :

  • 파일 목록에는 디렉토리가 아닌 파일 만 포함됩니다.
  • grep match 패턴의 슬래시는 부분 파일이 아닌 전체 파일 이름을 비교합니다.
  • 검색 패턴의 마지막 '$'는 줄의 끝과 일치하여 디렉토리 일치를 얻지 않고 전체 파일 이름 패치 만 가져옵니다.

통계는 정확한 위치가 필요합니까? 파일 이름 목록 이 있고 수많은 디렉토리에있을 수 있기 때문에 find를 사용 하고 있습니다. 명확하지 않으면 죄송합니다.
Andrew Stacey

흠. 나중에 경로가없는 파일 이름이 있다고 말하지 않았습니다! 어쩌면 대신 그 문제를 해결할 수 있습니까? 동일한 데이터 세트에서 여러 번 찾기를 실행하는 것보다 훨씬 효율적입니다.
Caleb

수정 해 주셔서 감사합니다. 구체적이지 않아 다시 한 번 죄송합니다. 파일 이름 / 경로는 해결하려는 것이 아닙니다. 파일은 두 시스템의 다른 위치에있을 수 있으므로 그 문제를 해결할 수있는 강력한 솔루션을 원합니다. 컴퓨터는 다른 방식이 아닌 사양에 맞게 작동해야합니다 ! 진지하게, 이것은 내가 자주하는 일이 아닙니다. 공간을 만들기 위해 삭제할 오래된 파일을 찾고 있었고 내 백업에 있는지 확인하기 위해 "빠른 '더러운"방법을 원했습니다.
Andrew Stacey

우선 전체 경로가 필요하지 않습니다. 백업하려는 디렉토리 구조에 대한 상대 경로입니다. 경로가 동일하지 않으면 파일이 동일하지 않을 가능성이 높고 테스트에서 오 탐지를 얻을 수 있다고 제안하겠습니다 . 솔루션이 빠른 것보다 더 더러운 것 같습니다. 당신이하지 않은 것을 생각해서 화상을 입는 것을보고 싶지 않습니다. 또한 파일이 처음에 백업하기에 충분한 가치가있는 경우, 1 차를 삭제하지 않아야합니다. 그렇지 않으면 백업을 백업해야합니다!
Caleb

Ak! 나는 질문에 초점을 맞추기 위해 많은 세부 사항을 생략했으며 당신은 그것들을 완벽하게 합리적이지만 완전히 틀린 가정으로 가득 채우고 있습니다! 내가 말할 것으로 충분 알고 파일이 이름의 특정 유형의 디렉토리에있는 경우 다음 내가 원래 파일인지 알고 내 컴퓨터에 복사본을 삭제하는 것이 안전합니다.
Andrew Stacey

1

첫 번째 간단한 접근 방식은 다음과 같습니다.

a) 파일 목록을 정렬하십시오.

sort file.lst > sorted.lst 
for f in $(< sortd.lst) ; do find -name $f -printf "%f\n"; done > found.lst
diff sorted.lst found.lst

누락 된 부분을 찾거나

comm sorted.lst found.lst

일치를 찾기 위해

  • 함정 :
    • 파일 이름의 줄 바꿈은 처리하기가 매우 어렵습니다.
    • 파일 이름의 공백과 비슷한 것들도 좋지 않습니다. 그러나 파일 목록의 파일을 제어 할 수 있기 때문에이 솔루션으로 이미 충분할 수 있습니다 ...
  • 단점 :

    • find는 파일을 찾으면 다른 파일과 다른 파일을 찾기 위해 계속 실행됩니다. 추가 검색을 건너 뛰는 것이 좋습니다.
    • find는 몇 가지 준비를 통해 한 번에 여러 파일을 검색 할 수 있습니다.

      -name a.file 또는 -name -b.file 또는 -name c.file 찾기 ...

옵션을 찾을 수 있습니까? 미리 정렬 된 파일 목록은 다음과 같이 가정합니다.

 for f in $(< sorted.tmp) ; do locate --regexp "/"$f"$" > /dev/null || echo missing $f ; done

foo.bar를 검색하면 파일 foo.ba 또는 oo.bar가 --regexp-construct와 일치하지 않습니다 (p가 없으면 정규 표현식으로 혼동하지 않아야 함).

찾기 위해 특정 데이터베이스를 지정할 수 있으며 가장 최근 결과가 필요한 경우 검색하기 전에 데이터베이스를 업데이트해야합니다.


1

나는 이것이 또한 유용 할 수 있다고 생각한다.

이것은 "목록"을 다른 폴더와 동기화하려는 실제 파일로 선택한 경우 한 줄 솔루션입니다.

function FUNCsync() { local fileCheck="$synchronizeTo/$1"; if [[ ! -f "$fileCheck" ]];then echo "$fileCheck";fi; };export -f FUNCsync;find "$synchronizeFrom/" -maxdepth 1 -type f -not -iname "*~" -exec bash -c 'FUNCsync "{}"' \; |sort

독서를 돕기 위해 :

function FUNCsync() {
  local fileCheck="$synchronizeTo/$1";
  if [[ ! -f "$fileCheck" ]];then 
    echo "$fileCheck";
  fi; 
};export -f FUNCsync;
find "$synchronizeFrom/" -maxdepth 1 -type f -not -iname "*~" -exec bash -c 'FUNCsync "{}"' \; |sort

이 예에서는 백업 "* ~"파일을 제외하고 일반 파일 유형 "-type f"로 제한합니다.


0
FIND_EXP=". -type f \( "
while read f; do
   FIND_EXP="${FIND_EXP} -iname $f -or"
done < file_list
FIND_EXP="${var%-or}"
FIND_EXP="${FIND_EXP} \)"
find ${FIND_EXP}

아마도?


0

쿼리 목록의 길이와 결과 목록의 길이를 단순히 비교하는 것이 어떻습니까?

while read p; do
  find . -name $p 2>/dev/null
done < file_list.txt | wc -l
wc -l file_list.txt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.