접미사 버전 없이 파일을 찾는 방법은 무엇입니까?

접미사 버전 없이 파일을 찾는 방법은 무엇입니까?

수백만 개의 파일이 있고 각 파일에 대해 버전( -> )을 생성 .jpg하고 싶습니다 . 이렇게 하려면 버전 없이 끝나는 모든 파일을 찾아야 합니다 ..jpg.webpfoo.jpgfoo.jpg.webp.jpg.jpg.webp

이제 저는 이렇게 합니다:

find "$path" -type f -iname "*.jpg" |
  while read -r image_path; do
      if [ ! -f "$image_path.webp" ]; then
        echo "$image_path"
      fi
  done |
  # treat only 10000 files per run
  head -n 10000 |
  ...

그러나 파이프를 사용하고 있으므로 하위 쉘이 생성됩니다. 이를 수행하는 더 효율적인 방법이 있는지 궁금합니다. 특히 생성하는 WebP 이미지가 많을수록 스크립트가 후보를 찾기 위해 경로를 필터링하는 데 더 많은 시간을 소비하기 때문입니다. just를 사용하여 이를 수행할 수 있는 방법이 있습니까 find?

우분투 20.04를 사용하고 있습니다. 파일은 하위 디렉터리에 배포됩니다.

답변1

나는 다음을 수행할 것입니다:

  1. 모든 접미사(예: *.jpg.webp) 파일을 찾아 정렬된 목록에 넣습니다.접미사를 제거하세요.;
  2. 접미사(예: )가 없는 모든 파일을 찾아 *.jpg두 번째 정렬 목록에 넣습니다.
  3. 두 목록을 비교하고 첫 번째 목록에서 항목을 제거합니다.
  4. 결과 Set Differences 목록에서 변환을 수행합니다.

그처럼

#!/bin/bash
comm -z -1 -3 \
   <(find -name '*.jpg.webp' -print0 | sed 's/\.webp\x0/\x0/g' | sort -z) \
   <(find -name '*.jpg'      -print0 | sort -z) \
| parallel -0 gm convert '{}' '{}.webp'

변환을 위해 GraphicsMagick을 사용하고 gm(내 경험상 속도와 안정성을 위해 ImageMagick을 선호함 convert) GNU가 설치되어 있다고 가정합니다 parallel(그렇지 않은 경우 xargs작동할 수 있음).

답변2

다음과 같이 시도해 보세요.

find "$path" -type f -iname "*.jpg" -exec \
  sh -c 'for f; do [ -e "$f.webp" ] || echo "$f" ; done' find-sh {} +

shARG_MAX(Linux의 경우 약 200만 바이트)로 제한되어 가능한 한 적은 횟수로 실행되며 (find에서 찾은 .jpg 파일 수에 따라 다름) while read ...모든 파일 이름을 명령줄 인수 주기로 전달하여 극심한 속도 저하를 방지합니다. 바라보다쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?그리고찾기 결과를 반복하는 것이 왜 나쁜 습관입니까?

이러한 파일의 배치를 효율적으로 처리하기 위해 출력을 파일로 리디렉션한 다음 10,000개(또는 필요한 수)의 배치로 분할합니다 split -l 10000.

참고: .jpg 파일 이름에 개행 문자가 포함된 경우 NUL을 구분 기호로 사용해야 하며, 그렇지 않은 경우 개행 문자를 구분 기호로 사용해야 합니다. NUL 구분 기호를 사용하려면 echo "$f"로 바꾸십시오 printf "%s\0" "$f". 그런데 splitNUL 구분 입력이 지원됩니다 -t '\0'.

배치를 처리하는 스크립트는 .jpg.webp버전을 빌드하는 데 필요한 모든 것을 실행하기 전에 파일 이름을 읽고 해당 파일이 존재하지 않는지(목록을 생성한 후 파일이 생성된 경우) 다시 확인해야 합니다 .jpg.webp.

파일 이름 구분 기호로 NUL을 사용해야 하는 경우 가장 쉬운 방법은 readarray(AKA mapfile)를 사용하여 전체 목록 배치를 배열로 읽고 파일 이름 배열을 반복하는 것입니다. 또는 awk 또는 perl을 사용하여 파일 이름을 처리합니다.

실제로 개행 문자를 구분 기호로 사용하더라도 배열을 사용하는 것이 while-read 루프보다 낫습니다.

답변3

이것은 직업처럼 들리네요 make. 누락되었거나 파일이 생성된 파일 이전에 수정된 파일만 생성합니다.

.PHONY: all
all: $(addsuffix .webp,$(shell find . -name '*.jpg'))

%.jpg.webp: %.jpg
    cwebp $< -o $@   #Some command that generates $@ from $<

이라는 파일에 저장 Makefile하고 실행해 보세요 make.
또는 make -j $(nproc)논리 코어만큼 많은 병렬 작업을 실행하세요. 아니면 명시적인 숫자를 선택하세요.물리적다른 작업을 위해 일부 여유 논리 코어를 남겨둡니다. )

파일이나 하위 디렉터리의 이름에 공백이 포함되어 있으면 중단됩니다.

%.jpg.webp: %.jpg패턴 규칙.

답변4

just를 사용하여 이를 수행할 수 있는 방법이 있습니까 find?

성능과 시간을 고려하지 않고 find이 작업을 수행하는 가장 간단한 명령은 다음과 같습니다.

find "$path" -type f -iname "*.jpg" ! -exec test -e '{}.webp' \; -print

다른 답변만큼 빠르지는 않지만 참고용일 뿐입니다.

그런데, 소문자로 끝나는 파일만 찾으려면 (대소문자 구분) 대신 (대소문자 구분)을 jpg사용하는 것이 더 좋습니다. 이는 특히 수백만 개의 파일에 대해 약간 느릴 수 있습니다.-name-iname

관련 정보