ディレクトリが存在する場合にのみディレクトリリストを圧縮する方法

ディレクトリが存在する場合にのみディレクトリリストを圧縮する方法

アーカイブしたいディレクトリのリストがあります。しかし、時にはそれらがすべて存在しない場合があります。

ディレクトリリストを提供してアーカイブを作成できるようにしたいのですが、これは既存のディレクトリのみをアーカイブし、見つからないディレクトリは無視します。 (ただし、ディレクトリが存在しない場合はまだ失敗します)

これは、継続的な統合を実行しており、特定のプロセスが後で使用するためにアーカイブにアーカイブしたい特定の成果物を生成するため、私にとって効果的です。生成できる可能性のあるすべてのパスを知っていますが、どのパスが何であるかは必ずしもわかりません。作られた。

可能なパスは次のとおりです。here_is_a_dir here_is_another_one yet_another_dir

私は通常、次のコマンドを使用してアーカイブを作成します。

tar -czf archive.tgz here_is_a_dir here_is_another_one yet_another_dir

もちろん、ディレクトリが見つからない場合は失敗します。

理想的には単純なコマンドでなければならず、完了するためにスクリプトは必要ありません。 (具体的には私の環境でのみ可能なので、他の人のように派手なシェルは使えませんが、shこれbashは私の環境によって異なり、これから変わることもあるので、他のシェルを使う答えもとても良いようです。)

答え1

使用される出力はls通常賢明ではなく安全ではありません。スペースや改行だけでなく、他のシェルメタ文字もファイル名やディレクトリ名で有効な文字であることを覚えておいてください。この問題は多くの場合に解決できますが、これを行うには通常、find作業に適したツール(たとえば)を使用するよりも多くの労力が必要です。

だからfind代わりにそれを使用してください。たとえば、

find . -maxdepth 1 -type d \( -name here_is_a_dir -o -name here_is_another_one \
  -o -name yet_another_dir \) -exec tar cfz archive.tgz {} +

-type dこれは、現在のディレクトリ()で一致するディレクトリ()を見つけ、.それをtarコマンドの引数として使用します。 toはORを使用\(して\)各サブ式を結合する式です-o(デフォルトでは、findの述語はAND結合です)。つまり、「最大深さ1 AND型ディレクトリAND(dir1 OR dir2 OR dir3)」と読み出されます。

優先順位を強制する括弧がない場合は、「最大深度 1 AND 型ディレクトリ AND dir1 OR(dir2 OR dir3)」と解釈され、既存のディレクトリの完全なリストは返されません。ほとんどの場合、dir1が存在するかどうかに応じて何も返さないか、dir3のみが返されます。

現在のディレクトリの下のサブディレクトリを見つけるには、この-maxdepth 1パラメータを削除します。

ディレクトリ一致で大文字と小文字を区別しない場合は、-iname代わりにを使用してください-name-nameorの引数は-iname固定文字列ではなくパターンにすることができます。これは、必要なディレクトリ名が非常に似ている場合に便利です。例えば

find . -maxdepth 1 -type d -iname 'dir[123]' -exec tar cfz archive.tgz {} +

これは-exec ....パイプtoと非常によく似ていますxargsが、toに組み込まれていますfind。実際にxargsしたい場合はすべて-exec-print0 | xargs -0 -r tar cfz archive.tgz。例えば

find . -maxdepth 1 -type d -iname 'dir[123]' -print0 | \
  xargs -0 -r tar cfz archive.tgz 

(これはNUL文字を出力区切り文字として使用するため、スペースなどを含むディレクトリ名と一緒に使用するのが安全です。-execこの-rオプションは、入力がなければxargsに何もしないように指示します)

答え2

そしてzsh

dirs_to_archive=(some/dir /some/other/dir and/more/dirs)
existing_dirs=($^dirs_to_archive(/N))
if (($#existing_dirs)); then
  tar -cf - -- $existing_dirs | xz > file.tar.xz
else
  echo >&2 Error: none of the dirs were found
fi

POSIX対応するコマンド(ただし、POSIXコマンドでもtarPOSIXxzコマンドでもない)は、次のようになります。

# The list of dirs in "$@" (the only array in POSIX sh language)
set -- some/dir /some/other/dir and/more/dirs

for dir do
  # remove from the array the elements that are not directories like with
  # zsh's / glob qualifier above
  [ -d "$dir" ] && [ ! -L "$dir" ] && set -- "$@" "$dir"
  shift
done
if [ "$#" -gt 0 ]; then
  tar -cf - -- "$@" | xz > file.tar.xz
else
  echo >&2 Error: none of the dirs were found
fi

答え3

Bashにアクセスできる場合は、拡張ワイルドカード(extglob)を使用してください。

$ shopt -s extglob; set -x
$ tar -czf archive.tgz *(here_is_a_dir|here_is_another_one|yet_another_dir)
+ tar -czf archive.tgz
tar: no files or directories specified

次のいずれかが存在する場合:

$ touch here_is_a_dir yet_another_dir
+ touch here_is_a_dir yet_another_dir
$ tar -czvf archive.tgz *(here_is_a_dir|here_is_another_one|yet_another_dir)
+ tar -czvf archive.tgz here_is_a_dir yet_another_dir
a here_is_a_dir
a yet_another_dir

set -x(グローブ拡張結果を確認するために使用しました。)

答え4

POSIX sh()でのみtar.sh使用する必要があるもの:

#!/bin/sh

first=1
for dir in "$@"; do
        if [ "$first" ]; then  # this is a bit ugly
                set --
                first=
        fi
        if [ -d "$dir" ]; then
                set -- "$@" "$dir"
        fi
done
echo tar -czf archive.tar.gz "$@"    # remove that 'echo'

テスト:

$ mkdir here_is_a_dir yet_another_dir
$ ./tar.sh here_is_a_dir here_is_another_one yet_another_dir
tar -czf archive.tar.gz here_is_a_dir yet_another_dir

関連情報