Processando valores de matriz no bash

3

Estou tentando criar uma matriz com base em nomes de arquivos e me meter em problemas com espaços em branco. Isso parece comum. Mas - tanto quanto eu posso ver - as aspas estão definidas corretamente, eu acho que deve ser a maneira como a matriz é construída.

to_dump="$(find . -maxdepth 1 -print0 )"
to_dump_array=($to_dump)

read -p " ->  " final
case "$final" in
   a) for drop in "${to_dump_array[@]}" ;
      do cp "$drop" --recursive --force Destination_Folder && \
      echo "dropped \"$drop\" ;
      done ;;
   b) echo "Won't drop anything" ;;
esac

Eu acho que deve haver uma maneira mais agradável de construir uma matriz a partir de uma consulta de localização. Além disso, onde mais eu estou errado?

    
por erch 13.07.2015 / 09:37

4 respostas

6

-print0 não deve ser usado em uma substituição $(...) , porque as sequências nas variáveis bash são terminadas por caractere nulo.

Eu fiz uma pergunta cuja resposta foi semelhante à que essa pergunta exige: link

Adapte essa resposta à sua pergunta:

to_dump=()
while IFS= read -r -d ''; do
  to_dump+=( "$REPLY" )
done < <(find . -maxdepth 1 -print0)

Isso cria uma matriz chamada to_dump e usa o comando read para ler elementos delimitados por NULL de find . O motivo que < <(...) está sendo usado aqui, em vez de um pipe, é evitar uma sub-camada implícita que impediria que o array fosse modificado.

Vale a pena notar que o seu comando find original provavelmente quer um -mindepth 1 , ou ele irá escolher . (o diretório atual) e você acabará fazendo uma cópia recursiva sobre isso.

Eu notei que você usa -maxdepth 1 como um argumento para encontrar, então talvez isso seja mais útil:

shopt -s nullglob
to_dump=( * .[!.]* ..?* )

Evitando find , isso usa somente anúncios internos, não é bifurcado e, na maior parte, é bastante limpo.

A primeira linha, shopt -s nullglob , é um comando bash (-only) que ativa a opção nullglob . Esta opção é descrita em man 1 bash :

If set, bash allows patterns which match no files (see Pathname Expansion above) to expand to a null string, rather than themselves.

Em termos mais simples, se você digitar * , mas não corresponder aos arquivos, ele removerá o * . O comportamento padrão é colocar o * lá de qualquer maneira.

A segunda linha adiciona 3 globs ao array:

  • * : todos os arquivos que não começam com .
  • .[!.]* : todos os arquivos que começam com um . e um caractere não . . Isso evita corresponder os diretórios . e .. .
  • ..?* : Todos os arquivos que começam com .. e pelo menos mais um caractere. Adicionado pela mesma razão que o glob anterior, cobrindo os casos perdidos.

O Bash expande os globs para a definição do array, e os expande corretamente - sem divisão no espaço em branco ou qualquer coisa assim.

Uma advertência sobre o uso do nullglob: Se você tiver ativado o nullglob, curl google.com/search?q=test resultará em queixar-se em você por não passar argumentos e ls /var/fasdfasafs* fornecerá uma listagem do diretório atual. Esta é uma das razões pelas quais não está ativada por padrão.

    
por 13.07.2015 / 10:05
3

Tente criar o array assim:

read -d $'
read -d $'%pre%' -r -a to_dump <<< $(find . -maxdepth 1 -print0)
' -r -a to_dump <<< $(find . -maxdepth 1 -print0)
    
por 13.07.2015 / 10:05
1
 find . -maxdepth 1

... parece-me indicar que você quer:

a=()
for f in ./..?* ./.[!.]* ./*
do  [ -e "$f" ] && a+=$f
done
    
por 13.07.2015 / 10:10
0

Não tenho certeza se o roteiro que você faz está fazendo o que você acha que está fazendo. Eu não acho que isso converte a saída terminada em nulo do find em uma matriz bash de nomes de arquivos:

to_dump_array=($to_dump)

Você verificou a saída do loop for para ver o que está recebendo?

for drop in "${to_dump_array[@]}"
do
    echo -e "$drop\n"
done

Há um estouro de pilha com algumas sugestões sobre o preenchimento de uma matriz de find -print0.

link

Você também pode estar usando melhor o índice da matriz para processar os itens da matriz, em vez de tentar atribuir à variável diretamente dentro do loop for, evita confiar na divisão do shell corretamente:

for drop in $(seq 0 $((${#to_dump_array[@]} - 1)))
do
    cp "${to_dump_array[$drop]}"

Isto pode não ser muito popular, mas se você precisar usar bash arrays e multiple escapes para gerenciar espaços em branco (entre outros caracteres incomuns) em nomes de arquivos, então você pode estar indo além do que o shell foi projetado. / p>

Você pode encontrar algo como python, perl, ruby para ser mais rápido, mais confiável e mais fácil de gerenciar e depurar a base de código.

Eu tenho um número de "bandeiras vermelhas" que eu uso para me dizer quando eu poderia ir além do que é sábio quando shell script.

  • Vários níveis de escape ou terminação nula para tratar todos os casos de nomes de arquivos.
  • Vários níveis de funções do shell chamando um ao outro.
  • Estruturas de dados complexas. Ou seja indo além de strings, números.
  • Eu me vejo fazendo "otimização de desempenho".
  • "Ruído na linha" nas expressões.

Sim, você certamente pode fazer todos os itens acima, mas deveria? Sério ... Python, perl, ruby etc. Em python é simples, e você não precisa se preocupar em escapar ou nomes de arquivos com espaços em branco ou caracteres binários.

import os

dirListing = os.listdir("somedirectory")

for eachEntry in dirlisting:
    doSomething
    
por 13.07.2015 / 12:05

Tags