"파일 이름이 있는 파일 B"에서 "파일 이름이 있는 파일 A", 즉 "A에서 제거된 항목"을 빼거나 빼지 않았습니까?

"파일 이름이 있는 파일 B"에서 "파일 이름이 있는 파일 A", 즉 "A에서 제거된 항목"을 빼거나 빼지 않았습니까?

rsync(할 수 있기 때문에) 큰 폴더 A를 B로 복사하거나 백업하고 싶습니다 .https://stackoverflow.com/a/65485164/1707015.

작동하지만 때로는 B를 정리하고 B에서 오래된 파일을 삭제하고 싶습니다(A에서 삭제된 경우).

A에서 삭제된 파일을 가져와야 합니다(B에서도 삭제할 수 있도록).

$ cat A_files.txt  # for example: think the small letters as paths like ./some/path/file.yaml
a
c
d
e
f

$ cat B_files.txt
a
b
c
d

$ \grep -f A_files.txt -F -v B_files.txt
b

(백슬래시는 \색상 등의 grep 별칭을 사용하지 않기 위한 것입니다.)

이것은 작동하지만 작은 파일에만 해당됩니다. 파일 이름당 100MB를 초과하는 파일의 경우 100GB 이상의 RAM이 필요합니다.

누군가 나에게 보다 리소스 효율적인 변형을 제공할 수 있습니까? 물론 가능합니다 rsync. 하지만 재미나 연습 목적으로 사용하기 위한 것은 아닙니다.

답변1

목록 결합/뺄셈의 경우 표준 명령은 입니다 comm. 정렬된 파일 라인에서 작동합니다.

B_files.txt따라서 행이 포함되지 않은 경우 A_files.txt:

export LC_ALL=C # for a simple and deterministic order and allow any byte
                # in file names.
comm -23 <(sort A_files.txt) <(sort B_files.txt)

파일이 이미 정렬된 경우:

comm -23 A_files.txt B_files.txt

이 접근 방식(또는 귀하의 접근 방식)은 파일 이름에 개행 문자가 포함될 수 있으므로 임의의 파일 이름에는 작동하지 않습니다.철사.

GNU 시스템을 사용하는 경우 NUL을 사용하여 행 대신 레코드를 구분하고 및 옵션을 사용할 수 있습니다 -z.sortcomm

또 다른 방법은 zsh의 배열 조합/뺄셈 연산자를 사용하는 것입니다.

cd /path/to/A || exit
A_regular_files=(**/*(ND.))
cd /path/to/B || exit
B_regular_files=(**/*(ND.))

files_in_B_but_not_in_A=(${B_regular_files:|A_regular_files})

또한 이 -x옵션을 전달하지 않으면 grep하위 문자열이 일치합니다 . 예를 들어 grep -F foo/bar일치합니다.blah/foo/barrage

답변2

나는 다음을 생각해 냈습니다.

MY_SOURCE=A_files.txt
readarray -t MY_TARGET_ARRAY < B_files.txt
for LINE in "${MY_TARGET_ARRAY[@]}"; do
    if ! grep -q "${LINE}" "${MY_SOURCE}"; then
        echo "${LINE}";
    fi;
done

아직 테스트되지 않았습니다. 누락된 문제 -x및/또는 누락된 -F문제 가 있을 수 있습니다 grep.

관련 정보