web-dev-qa-db-ja.com

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

アーカイブしたいディレクトリのリストがあります。しかし、すべてが存在するわけではありません。

ディレクトリのリストを提供することによってアーカイブを作成できるようにしたいのですが、それは既存のものだけをアーカイブし、欠落しているものは無視します。 (ただし、ディレクトリが存在しない場合でも失敗するはずです)

継続的インテグレーションを実行していて、一部のプロセスが特定のアーティファクトを作成してアーカイブに将来保持したいので、これは私にとって便利です。作成されます。

可能なパスは次のとおりです:here_is_a_dirhere_is_another_oneyet_another_dir

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

tar -czf archive.tgz here_is_a_dir here_is_another_one yet_another_dir

もちろん、いずれかのディレクトリが欠落している場合、どちらが失敗します。

理想的には、それは単純なコマンドであり、スクリプトを実行する必要はありません。 (具体的には、私の環境ではshしか使用できないため、bashなどのファンシーなシェルは使用できませんが、これは私の環境に固有であり、将来変更される可能性があります。他のシェルを使った答えもおそらく良いと思います。)

7
Inbar Rose

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コマンドの引数として使用します。 _\(_から_\)_は、_-o_を使用して各部分式がORで結合された式です(デフォルトでは、findの述語はANDで結合されます)。つまり、「maxdepth 1 ANDタイプディレクトリAND(dir1 OR dir2 OR dir3)」として読み取られます。

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

現在のディレクトリの下にあるサブディレクトリを見つけるには、_-maxdepth 1_引数を削除します。

ディレクトリの一致で大文字と小文字を区別しない場合は、_-iname_ではなく_-name_を使用します。 _-name_または_-iname_の引数は、固定文字列ではなくパターンにすることができます。これは、目的のディレクトリ名が非常に似ている場合に便利です。例えば.

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

_-exec ...._はxargsへのパイプとよく似ていますが、findに組み込まれています。実際、_-exec_以降のすべてを_-print0 | xargs -0 -r tar cfz archive.tgz_で置き換える場合は、xargsを使用できます。例えば.

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

(これは出力セパレータとしてNUL文字を使用するため、_-exec_を使用するのと同じように、スペースなどを含むdirnameで使用しても安全です。_-r_オプションは、xargsに何も実行しないように指示します入力)

8
cas

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(tarxzもPOSIXコマンドではないことに注意してください)は次のようになります。

# 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
7

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

そして、それらの1つ以上が存在する場合:

$ 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なので、グロブ拡張の結果を確認できます。)

3
muru

これは、lsを使用して既存のディレクトリのリストを作成し、xargsを介してtarコマンドにパイプすることで比較的簡単に実行できます。

ソリューション:

ls -d here_is_a_dir here_is_another_one yet_another_dir 2> /dev/null | xargs tar -czf archive.tgz

内訳:

  • ls -dディレクトリのみを一覧表示
  • here_is_a_dir here_is_another_one yet_another_dirチェックするディレクトリのリスト
  • 2> /dev/null stderrを/ dev/nullにパイプして、stdout出力のみを取得します(dirsが欠落していない)
  • | xargsは、前のコマンドの既存のディレクトリのリストを引数に変換します
  • tar -czf archive.tgz引数を使用してアーカイブを作成します
1
Inbar Rose

tarのバージョンがサポートしている場合は、空のtarファイルを作成し、-rではなく-cを使用して、必要に応じて追加します。

touch foo.tar

for d in here_is_a_dir here_is_another_one yet_another_dir; do
  if [ -d "$d" ]; then
    tar -rf foo.tar "$d"
  fi
done
gzip foo.tar
0
chepner

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
0
ilkkachu

pax がある場合、これを使用して、アーカイブに含まれるものをフィルタリングできます。 Paxは、歴史的に多様なcpioとtarの代替としてPOSIXによって義務付けられていますが、多くのUnixバリアントはその採用に抵抗しており、特にBusyBoxには採用されていません。

Paxには、ファイルの除外機能がありますが、説明では隠されています。 -sを使用してファイルの名前を変更できます。ファイルの名前を空の文字列に変更すると、そのファイルは除外されます。複数の-sオプションを使用すると、最初の一致ごとに適用され、-sフィルターの名前変更はスキップされます。このようにして、一連の包含/除外ルール rsyncと同様 を構築できます。 Pax構文の包含規則は-s'!REGEX!&!'(それ自体に名前変更)であり、除外規則は-s'!REGEX!!'(空に名前変更)です。

pax -w -x ustar \
    -s'!^\./here_is_a_dir$!&!' -s'!^\./here_is_a_dir/!&!' \
    -s'!^\./here_is_another_one$!&!' -s'!^\./here_is_another_one/!&!' \
    -s'!^\./yet_another_dir$!&!' -s'!^\./yet_another_dir/!&!' \
    . | gzip >archive.tgz

BSD tarには、同様の-sオプションがあります。一方、GNU= tarとBusyBox tarには、これを行う/体操を除外する方法がありません。

最も単純なのはおそらく

find adir anotherdir yadir -maxdepth 0 2>&- | tar Tcf - my.tar
0
jthill