다음과 같은 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를 제거합니다(제가 이해한 대로).