bash 3.2でグローバル拡張子を使用して複数のファイル配列をコピーする

bash 3.2でグローバル拡張子を使用して複数のファイル配列をコピーする

私がやりたいことをすべてウェブで見つけましたが、私のユースケースに正確に合うものは何もありませんでした。

複数の特定のファイルと特定のファイル拡張子を持つファイルをデスクトップのディレクトリにコピーするスクリプトを作成しようとしています。また、bash 3.2でも実行する必要があります。

この行は私を転倒させる行です。パターンの一致、順序、拡張、およびワイルドカードに関連する可能性があることを知っています。

   #!/usr/bin/env bash

  declare -a backup_files=(*.{id,nsf}, "desktop8.ndk", "archive", "user.dic");
  declare -a notes_data_directory=~/Library/Application\ Support/IBM\ Notes\ Data/;
  declare -a notes_backup_directory=~/Desktop/Notes\ Backup;

  mkdir "$notes_backup_directory";
  cd "$notes_data_directory";

  for i in "${backup_files[@]}"
  do
    cp -nipvR "$notes_data_directory$i" "$notes_backup_directory";

    # Also tried this
    # find "$notes_data_directory""$i" -exec cp -nipvR {} "$notes_backup_directory" \;
  done

  cd -

私がこれを行うと、echo "$backup_files"次のような結果が得られます。

*.{id,nsf}, desktop8.ndk, user.dic

私はそれが構文に関連していると確信しています。私はどこにいるのか分からない。

ベストアンサー1

特定のテキストの周りに引用符を入れると、ワイルドカード拡張や中括弧拡張などの拡張は無効になります。これを覚えておくための論理的な方法は、単語リストを生成しますが、引用文などの文脈を介して内容が単一の単語であることを保証することです。

また、偽のカンマがあります。シェルスクリプトでは、単語はスペースで区切られます。

declare -a backup_files=(*.{id,nsf} "desktop8.ndk" "user.dic")

次のように書くことができます。

backup_files=(*.{id,nsf} "desktop8.ndk" "user.dic")

backup_files関数がローカル変数として宣言しない限り。

パターンの1つがファイルと一致しない場合は、拡張されていないままです。たとえば、空のディレクトリでは、および4つの要素で終わります*.id。これをに渡したいなら大丈夫です。より一般的には、処理中に各要素が既存のファイルであることを確認することでこの問題を解決できます。*.nsfdesktop8.ndkuser.dicrm -f

for x in "${backup_files[@]}"; do
  if [[ ! -e "$x" ]]; then continue; fi
done

(余談として、配列の要素にアクセスすることは、引用符付き文字列が単語リストではなく単語を生成するという規則の例外であることに注意してください。配列拡張を使用すると、二重引用符は次のことを保証します。各要素パターンマッチングと単語分割を実行するのではなく、個々の単語で終了します。"$@"のようなバリエーションを含む唯一の例外です。 )

またはnullglob、一致するファイルがない場合は、パターン一致が空のリストを生成するようにオプションを設定できます。

shopt -s nullglob
backup_files=(*.{id,nsf} "desktop8.ndk" "archive" "user.dic")

これにより、配列には既存の.id合計.nsfファイルのみが含まれますが、とにかく合計が含まれますdesktop8.ndkuser.dicこれらのファイルが存在しない場合は、パターンに設定して除外することもできます。

shopt -s nullglob
backup_files=(*.{id,nsf} desktop8.nd[k] archiv[e] user.di[c])

配列を定義すると、中かっことワイルドカードが拡張されます。したがって、パターンは現在のディレクトリのファイルと一致します。スクリプトが意図しない奇妙なことをしています。現在、ディレクトリからファイル名を収集し、別のディレクトリから同じ名前のファイルをコピーしています$notes_data_directory。別のディレクトリにあるファイルを一致させるには、次のように言う必要があります。

notes_data_directory=~/Library/Application\ Support/IBM\ Notes\ Data/
notes_backup_directory=~/Desktop/Notes\ Backup
backup_files=("$notes_data_directory/"*.{id,nsf} "$notes_data_directory/desktop8.ndk" "$notes_data_directory/archive" "$notes_data_directory/user.dic")
for i in "${backup_files[@]}"
do
  cp -nipvR "$i" "$notes_backup_directory"
done

中括弧拡張を使用して定義を短縮できますbackup_files

backup_files=("$notes_data_directory/"{*.{id,nsf},desktop8.ndk,archive,user.dic})

または、コピーする前にディレクトリに変更してください。

notes_data_directory=~/Library/Application\ Support/IBM\ Notes\ Data/
notes_backup_directory=~/Desktop/Notes\ Backup
cd "$notes_data_directory"
shopt -s nullglob
backup_files=(*.{id,nsf} desktop8.nd[k] archiv[e] user.di[c])
for i in "${backup_files[@]}"
do
  cp -nipvR "$i" "$notes_backup_directory"
done

別のアプローチは、パターンのリストを配列に保存し、引用符なしの変数を使用してパターンを拡張することです。ソース行を解析するときは、中かっこ拡張が発生し、ワイルドカードは展開されていない状態で配列に保存される必要があります。パターンにスペースが含まれている場合は、引用符で囲む必要があります。これを行うのは少し面倒です。私はこのアプローチをお勧めしません。フォローするのは難しいです。パターンは最終的にファイルと一致する場合と一致しない場合がありますが、少なくともcp1つのソースファイルが必要なため、パターンがファイルと一致しない場合は別々に処理する必要があります。

notes_data_directory=~/Library/Application\ Support/IBM\ Notes\ Data/
notes_backup_directory=~/Desktop/Notes\ Backup
backup_patterns=(\*.{id,nsf} "desktop8.nd[k]" "archiv[e]" "user.di[c]")
# equivalent to backup_patterns=("*.id" "*.nsf" "desktop8.nd[k]" "archiv[e]" "user.di[c]")
shopt -s nullglob
for i in "${backup_patterns[@]}"
do
  files=("$notes_data_directory/"$i)
  if [[ ${#files[@]} -ne 0 ]]; then
    cp -nipvR "${files[@]}" "$notes_backup_directory"
  fi
done

おすすめ記事