web-dev-qa-db-ja.com

配列変数を渡すときに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
willDurango

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

findコマンドには正しく使用したい-nameオプションがありますが、使用できるファイル名パターンは1つだけです。スクリプトのユーザーが複数のファイル名サフィックスを指定しているため、サフィックスと同じ数の-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 \( "$@" \)

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

このスクリプトを次のように呼び出す場合

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からfindを呼び出すと、tarが複数回呼び出される可能性があるためです。 tar -cを使用して新しいアーカイブを作成した場合、そのアーカイブはtarが呼び出されるたびに切り捨てられます(findが数千のファイルを検出した場合は何度も発生する可能性があります)。代わりにtar -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" {} +

上記のスクリプトでの引用符の使用は非常に慎重であることに注意してください。スペース、改行、その他の異常な文字を含む名前を含む、許可されたファイル名でファイルをアーカイブすることが可能になります。

関連:


-print0を持つfind実装を使用する場合は、以下のスクリプトのように、見つかったパス名を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 -

-print0を使用すると、findはnulで区切られたパス名を出力し、GNU tar--null -T -オプションで読み取ります。


bash固有のスクリプトとしての最後のスクリプト(配列を使用、-nameオプションにはnames):

#!/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 -
1
Kusalananda

zshおよびGNU 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}_:拡張をグロブパターンとして扱います(位置パラメーターがjpggif、_jpg|gif|\*_の場合、現在は_*_のように見えます)
  • _**/_:任意のレベルのサブディレクトリ
  • _pattern~$output_:出力ファイル自体をglob展開から除外します
  • _(D.)_:glob qualifiers:隠しファイルを含め、通常のファイルのみを選択します。
0