Copie o array de múltiplos arquivos com extensões globbed, no bash 3.2

1

Eu encontrei partes e pedaços de cada uma das coisas que quero fazer na Web, mas nada se encaixa exatamente no meu caso de uso.

Estou tentando escrever um script que copie vários arquivos específicos para um diretório na área de trabalho, bem como arquivos com uma determinada extensão de arquivo. Também tem que correr na festa 3.2.

Esta linha é aquela que me deixa louco. Eu percebo que provavelmente tem que fazer algo com correspondência de padrões, ordem, expansão e globbing.

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

Quando eu echo "$backup_files" é isso que recebo:

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

Tenho certeza que tem a ver com a sintaxe. Eu só não sei onde.

    
por Kevin Suttle 19.07.2015 / 19:28

1 resposta

1

Colocar citações em algum texto desativa as expansões, como expansão de curingas e expansão de chaves. Uma maneira lógica de lembrar disso é que eles produzem uma lista de palavras, mas contextos como as citações garantem que o conteúdo seja uma única palavra.

Além disso, você tem vírgulas espúrias. Em um script de shell, as palavras são separadas por espaços.

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

Você pode escrever isso como

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

a menos que você também queira declarar backup_files como uma variável local em uma função.

Observe que, se um dos padrões não corresponder a nenhum arquivo, ele não será expandido. Por exemplo, em um diretório vazio, você terminará com os quatro elementos *.id , *.nsf , desktop8.ndk e user.dic . Se você só vai passar isso para rm -f , não importa. Mais geralmente, você pode lidar com isso verificando se cada elemento é um arquivo existente durante o processamento, por exemplo,

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

(Incidentalmente, note que acessar os elementos de uma matriz é uma exceção à regra que uma string entre aspas resulta em uma palavra em vez de uma lista de palavras. Com a expansão da matriz, as aspas duplas asseguram que cada elemento termina como uma única palavra, em vez de passar por correspondência de padrões e divisão de palavras. Isso, incluindo variantes como "$@" , é a única exceção.

Como alternativa, você pode definir a opção nullglob para tornar o resultado de correspondência de padrão em uma lista vazia quando não corresponder a nenhum arquivo.

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

Em seguida, a matriz conterá apenas os arquivos .id e .nsf existentes, mas conterá desktop8.ndk e user.dic , não importa o quê. Você pode organizar para excluir esses arquivos, se eles não existirem, tornando-os um padrão também.

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

Observe que as chaves e o caractere curinga são expandidos quando a matriz é definida. Assim, os padrões correspondem aos arquivos no diretório atual. Seu script está fazendo algo estranho e, provavelmente, não intencional: você está reunindo nomes de arquivos no diretório atual e copiando arquivos de nome idêntico de um diretório diferente $notes_data_directory . Se você quiser combinar arquivos em um diretório diferente, você precisa dizer isso.

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

Você pode usar a expansão de chaves para reduzir a definição de backup_files :

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

Alternativamente, mude para o diretório antes de copiar.

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

No entanto, outra abordagem é armazenar uma lista de padrões na matriz e usar a variável sem aspas para expandir os padrões. Observe que a expansão da chave precisa ocorrer no momento em que a linha do código-fonte é analisada, enquanto os curingas precisam ser armazenados não expandidos na matriz. Se houver algum espaço em branco no padrão, ele precisa ser citado; acertar isso é um pouco complicado. Eu não recomendo essa abordagem, é difícil de seguir. Como um padrão pode ou não acabar correspondendo a um arquivo, mas cp requer pelo menos um arquivo de origem, é necessário manipular o caso em que o padrão corresponde ao arquivo zero separadamente.

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
    
por 20.07.2015 / 00:32