Variáveis bash com espaço (diretórios)

6

Estou tentando fazer eco de todos os arquivos em /foo , preservando os espaços nos diretórios listados. Atualmente, um diretório chamado "bar1 bar2" será exibido como /path/to/foo/bar1/ , em seguida, /bar2 , em seguida, file.txt em novas linhas. Preciso ecoar /path/to/foo/bar1 bar2/file.txt ou /path/to/foo/bar1\ bar2/file.txt .

for file in $(find /path/to/foo/ -type f); do
  if [[ ... ]]; then
    echo "${file}"
  fi
done

Também tentei:

for file in $(find /path/to/foo/ -type f | sed 's/ /\ /g'); do
  if [[ ... ]]; then
    echo "${file}"
    echo "$file" # variations i've tried
    echo $file   # variations i've tried
    echo ${file}
    otherCommand "${file}"
  fi
done
    
por super-frustrated 24.08.2017 / 07:01

1 resposta

14

O problema não é com $file , é com o uso de $(find…) .

As variáveis regulares e as substituições de $(…) do processo estão sujeitas exatamente ao mesmo tipo de divisão de palavras. Se você colocar $(find…) entre aspas, você obtém uma palavra única com todas as saídas. Se você não o fizer, ele será dividido em qualquer espaço em branco - não apenas as linhas novas ou algum outro limite mágico que você provavelmente esperava.

Uma parte importante do que foi dito acima é que escapes de barra invertida não são processados quando a variável é expandida. É apenas dividido em espaços e é isso.

(Em outras palavras, citar $file não ajudou porque nunca teve o valor correto em primeiro lugar!)

Se você quiser processar todos os arquivos recursivamente, suas opções são:

  1. Ler a saída de find de outra maneira:

    find … -print | while read -r file; do
        echo "Got $file"
    done
    

    (Nota lateral: os nomes dos arquivos podem incluir tecnicamente também novas linhas, por isso você pode querer evitar isso, por mais raro que seja:

    find … -print0 | while read -r -d "" file; do
        echo "Got $file"
    done
    
  2. Para pequenas quantidades de arquivos, use os curingas estendidos do bash:

    shopt -s globstar
    for file in /path/to/foo/**; do
        echo "Got $file"
    done
    

Com diretórios grandes, a vantagem de find | while read é que ele é transmitido - seu script processará os resultados conforme eles são exibidos. Enquanto isso, $(find) e curingas têm que coletar tudo na memória primeiro, e só então retornar os resultados (possivelmente massivos).

Por outro lado, o uso de um pipe afeta o loop inteiro while (não apenas o comando read ), portanto, se você quiser executar qualquer coisa que quer ler a entrada do teclado, você tem que fornecer manualmente o stdin original, por exemplo vim "$file" < /dev/tty .

    
por 24.08.2017 / 07:12