모든 파일을 gzip에서 xz로 동적으로(재귀적으로) 변환하는 방법은 무엇입니까?

모든 파일을 gzip에서 xz로 동적으로(재귀적으로) 변환하는 방법은 무엇입니까?

다음과 같은 gzip 파일이 포함된 디렉토리 트리가 있습니다.

basedir/a/file.dat.gz
basedir/b/file.dat.gz
basedir/c/file.dat.gz
etc.

각 파일을 디스크에 압축 해제하지 않고 단일 명령을 사용하여 이러한 모든 파일을 gzip에서 xz로 어떻게 변환할 수 있습니까?

디스크에 압축을 풀기 위한 간단한 두 줄의 코드는 다음과 같습니다.

find basedir/ -type f -name '*.dat.gz' -exec gzip -d {} \;
find basedir/ -type f -name '*.dat' -exec xz {} \;

첫 번째 명령은 더 짧을 수 있습니다.gunzip -r *

단일 파일의 경우 즉시 변환이 간단합니다(단, .gz 파일을 대체하지는 않음).

gzip -cd basedir/a/file.dat.gz | xz > basedir/a/file.dat.xz

gzip과 xz는 확장 자체를 처리하므로 다음과 같이 말하고 싶습니다.

gunzip -rc * > xz

find | xargs basename -s .gz { }조금 보았지만 작동하는 해결책을 찾지 못했습니다.

쉘 스크립트를 작성할 수도 있지만 간단한 해결책이 있어야 한다고 생각합니다.


편집하다

답변해주신 모든 분들께 감사드립니다. 나는 우리 모두가 결코 실패하지 않는 명령™을 좋아한다는 것을 알고 있습니다. 따라서 일을 단순하게 유지하려면 다음을 수행하십시오.

  • 모든 하위 디렉터리에는 숫자, 문자(단, äöü), 밑줄 및 빼기 기호만 포함됩니다.
  • 모든 파일의 이름은 file.dat[.n].gz로 지정됩니다. n은 양의 정수입니다.
  • 어떤 디렉터리나 파일에도 ".gz"가 없습니다(최종 파일 접미사 제외).
  • 이것이 이 디렉토리에 포함된 유일한 콘텐츠입니다.
  • 이름 지정을 제어하고 필요에 따라 제한할 수 있습니다.

간단한 find -exec ...OR을 사용하여 ls | xargs찾은 파일 이름의 ".gz"를 즉시 ".xz"로 바꿀 수 있는 명령이 있습니까? 그런 다음 다음과 같이 작성할 수 있습니다(의사).

find basedir/ -type f -name '*.gz' -exec [ gzip -cd {} | xz > {replace .gz by .xz} \; ]

답변1

find . -name '*.gz' -type f -exec bash -o pipefail -Cc '
  for file do
    gunzip < "$file" | xz > "${file%.gz}.xz" && rm -f "$file"
  done' bash {} +

-C기존 파일 덮어쓰기 및 심볼릭 링크를 따르지 않는 것을 방지합니다 .와는 별개로기존 파일이 비표준 파일이거나 비표준 파일에 대한 링크인 경우 a file.gz및 a 에 대한 file.xz심볼릭 링크가 없으면 데이터가 손실되지 않습니다 /dev/null. 이를 방지하려면 구현된 zsh일부 -execdir기능을 사용하여 find좋은 측정값을 얻고 일부 경쟁 조건을 피할 수 있습니다.

find . -name '*.gz' -type f -execdir zsh -o pipefail -c '
  zmodload zsh/system || exit
  for file do
    gunzip < "$file" | (
      sysopen -u 1 -w -o excl -- "${file%.gz}.xz" && xz) &&
      rm -f -- "$file"
  done' zsh {} +

또는 재압축이 실패하면 파일을 정리합니다 xz.

find . -name '*.gz' -type f -execdir zsh -o pipefail -c '
  zmodload zsh/system || exit
  for file do
    sysopen -u 1 -w -o excl -- "${file%.gz}.xz" &&
      if gunzip < "$file" | xz; then
        rm -f -- "$file"
      else
        rm -f -- "${file%.gz}.xz"
      fi
  done' zsh {} +

짧게 만들고 이러한 잠재적인 문제 중 일부를 무시할 준비가 되어 있으면 다음을 zsh수행할 수 있습니다 .

for f (./**/*.gz(D.)) {gunzip < $f | xz > $f:r.xz && rm -f $f}

답변2

나는 단순한 for루프를 좋아한다..

for file in basedir/*/*.gz
do
    gzip -cd < "$file" | xz > "${file%%.gz}.xz"
done

...적어도 디렉토리 구조가 충분히 규칙적이고 단순하다면 말이죠. 알 수 없는 깊이로 이동해야 하거나 파일 선택에 추가 조건이 있는 경우에도 계속 유지하거나 find유사해야 합니다.

답변3

find basedir/ -type f -name '*.dat.gz'|while read -r line; do
 gzip -cd "$line" | xz > ${line%.gz}.xz
 rm "$line"
done

답변4

find 및 Parallel을 사용하여 이 작업을 수행할 수 있습니다.

parallel -0 'gzip -cd '{}' | xz > '{.}'.xz; rm '{}'' < <(find basedir -iname \*gz -print0)

완료된 단계:

  • gz로 끝나는 모든 파일을 재귀적으로 찾습니다(대소문자 구분 안 함).
  • 프로세스 대체의 표준 입력
  • 병렬 gzip foo.gz | xz > {foo}.xz;
    • {.} foo.gz에서 .gz를 제거합니다(제가 이해한 대로).

관련 정보