次のgzipファイルを含むディレクトリツリーがあります。
basedir/a/file.dat.gz
basedir/b/file.dat.gz
basedir/c/file.dat.gz
etc.
各ファイルをディスクに解凍せずに単一のコマンドを使用して、これらのファイルをすべてgzipからxzに変換する方法は?
ディスクに解凍するための簡単な2行のコードは次のとおりです。
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
既存のファイルの上書きやシンボリックリンクに従わないようにします。とは別に既存のファイルが非標準ファイルまたは非標準ファイルへのリンクである場合、afile.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を削除します(私が理解したように)。