tar配列変数を渡すと、「計算できません:ディレクトリにファイルがありません」

tar配列変数を渡すと、「計算できません:ディレクトリにファイルがありません」

ディレクトリにファイルのアーカイブ(tar)を生成するbashスクリプトを作成しようとしています。このようなbashスクリプト(./backup.bash pdf txt bak)を呼び出すときは、ファイル拡張子をパラメータとして渡す必要があります。私はこれらのパラメータを格納するために配列を使用しています。 (ls | grep -i {array})は、入力配列のファイル拡張子と一致するディレクトリ内のすべてのファイルを見つけ、見つかったファイルを一覧表示します。 (find . -type)これらの拡張子は、その拡張子に関連付けられているファイルを見つけるために使用されます。 (tar cvf)は、作業ディレクトリにbackup.tarというバックアップファイルを作成します。 tarコマンドの最後に配列をリストしましたが、今考えてみると、findコマンドをtarコマンドにパイプすることができるようです。

my_array=([$@])
ls | grep -i \{my_array[$@]}
find . -type f \( -name "my_array[$@]" \)
tar cvf ./PATH/backup.tar my_array[$@]

答え1

この問題を解決するには、2つの主な問題があります。ユーザーが指定した特定のファイル名サフィックスを含むすべてのファイルを見つけてアーカイブに追加する必要がありますtar

このfindコマンドには-name使用したいオプションがありますが、単一のファイル名パターンのみを使用します。スクリプトユーザーは複数のファイル名のサフィックスを提供するため、サフィックスの数と同じ数の-nameオプションを使用する必要があります。

つまり、-name "PATTERN"複数のオプションの配列を構成し、-o各オプションの間に中間値がなければなりません(それらの間の論理「OR」を表します)。その後、find指定されたファイル名サフィックスを含むファイル名を検索するために使用されます。

これは配列を変更することによって行われます$@

#!/bin/sh

for suffix do
    shift
    set -- "$@" -o -name "*.$suffix"
done
shift    # remove the very first "-o" from $@

find . -type f \( "$@" \)

これは$@、コマンドラインで指定されたサフィックスが最初からすでに含まれている配列を変更します。ループから前の要素を削除し$@、配列の末尾に単語を挿入します。

このスクリプトが呼び出されると

sh script.sh sh txt c

find次のコマンドを設定します。

find . -type f \( -name '*.sh' -o -name '*.txt' -o -name '*.c' \)

これにより、関連ファイルがすべて検索されます。これでアーカイブに追加するだけです。

GNU tar(例:BSDではないtar)の場合、このr操作を通じて更新または作成アーカイブ(BSDはtar更新のみを行い、新しいアーカイブを作成しません)。

backup=./PATH/backup.tar
rm -f "$backup"
find . -type f \( "$@" \) -exec tar -r -v -f "$backup" {} +

./PATH/backup.tarこれにより、関連ファイルを含むアーカイブが作成されます。

私が使用しない理由tar -cは、これを呼び出すと何度も呼び出される可能性tarがあるためです。過去に新しいアーカイブを作成すると、呼び出されるたびに切り捨てられます(何千ものファイルが見つかると複数回発生する可能性があります)。代わりに、我々は引き続きアーカイブを更新しています。findtartar -ctarfindtar -r

したがって、スクリプト全体は次のようになります。

#!/bin/sh

backup=./PATH/backup.tar

if [ "$#" -eq 0 ]; then
    echo 'No filename suffixes given' >&2
    exit 1
fi

for suffix do
    shift
    set -- "$@" -o -name "*.$suffix"
done
shift    # remove the very first "-o" from $@

rm -f "$backup"
find . -type f \( "$@" \) -exec tar -r -v -f "$backup" {} +

上記のスクリプトで引用符を使用したのは意図的なものです。スペース、改行、その他の珍しい文字を含む名前など、許可されたファイル名を使用してファイルをアーカイブできます。

関連:


findを含む実装を使用している場合は、次のスクリプトに示すように、見つかった-print0パス名をGNUに渡すこともできます。tar

#!/bin/sh

backup=./PATH/backup.tar

if [ "$#" -eq 0 ]; then
    echo 'No filename suffixes given' >&2
    exit 1
fi

for suffix do
    shift
    set -- "$@" -o -name "*.$suffix"
done
shift    # remove the very first "-o" from $@

find . -type f \( "$@" \) -print0 | tar -c -v -f "$backup" --null -T -

を使用すると、-print0GNUがオプションを使用して読み取ることができるfindnulで区切られたパス名が出力されます。tar--null -T -


bash特定のスクリプトとしての最後のスクリプト(配列をオプションnamesとして使用-name):

#!/bin/bash

backup=./PATH/backup.tar

if [ "$#" -eq 0 ]; then
    echo 'No filename suffixes given' >&2
    exit 1
fi

names=( -name "*.$1" )
shift
for suffix do
    names+=( -o -name "*.$suffix" )
done

find . -type f \( "${names[@]}" \) -print0 | tar -c -v -f "$backup" --null -T -

答え2

zshGNUで使用するか、次の手順を実行tarしますbsdtar

#! /bin/zsh -
set -o extendedglob
output=file.tar.gz
printf '%s\0' **/*.(${(j:|:)~${(b)@}})~$output(D.) |
  tar --null -cf - -T - | xz > $output
  • ${(b)@}:パターンとして扱われないように位置パラメータを引用します。
  • ${(j:|:)...}:結果の単語を次に関連付けます。|
  • ${~var}:拡張をワイルドカードパターンとして処理します(現在jpg|gif|\*位置パラメータがjpg、、、gifのように見えます*)。
  • **/: すべてのレベルのサブディレクトリ
  • pattern~$output:glob 拡張子から出力ファイル自体を除外します。
  • (D.):glob修飾子:隠しファイルを含み、通常のファイルのみを選択します。

関連情報