Nomes de arquivos com espaços quebrando para loop, comando find

29

Eu tenho um script que pesquisa todos os arquivos em várias subpastas e arquivos para tar. Meu script é

for FILE in 'find . -type f  -name '*.*''
  do
if [[ ! -f archive.tar ]]; then

  tar -cpf archive.tar $FILE
else 
  tar -upf archive.tar $FILE 
fi
done

O comando find me fornece a seguinte saída

find . -type f  -iname '*.*'
./F1/F1-2013-03-19 160413.csv
./F1/F1-2013-03-19 164411.csv
./F1-FAILED/F2/F1-2013-03-19 154412.csv
./F1-FAILED/F3/F1-2011-10-02 212910.csv
./F1-ARCHIVE/F1-2012-06-30 004408.csv
./F1-ARCHIVE/F1-2012-05-08 190408.csv

Mas a variável FILE armazena apenas a primeira parte do caminho ./ F1 / F1-2013-03-19 e, em seguida, a próxima parte 160413.csv .

Eu tentei usar read com um loop while,

while read 'find . -type f  -iname '*.*'';   do ls $REPLY; done

mas recebo o seguinte erro

bash: read: './F1/F1-2013-03-19': not a valid identifier

Alguém pode sugerir uma forma alternativa?

Atualizar

Como sugerido nas respostas abaixo, atualizei os scripts

#!/bin/bash

INPUT_DIR=/usr/local/F1
cd $INPUT_DIR
for FILE in "$(find  . -type f -iname '*.*')"
do
archive=archive.tar

        if [ -f $archive ]; then
        tar uvf $archive "$FILE"
        else
        tar -cvf $archive "$FILE"
        fi
done

A saída que recebo é

./test.sh
tar: ./F1/F1-2013-03-19 160413.csv\n./F1/F1-2013-03-19 164411.csv\n./F1/F1-2013-03-19 153413.csv\n./F1/F1-2013-03-19 154412.csv\n./F1/F1-2012-09-10 113409.csv\n./F1/F1-2013-03-19 152411.csv\n./.tar\n./F1-FAILED/F3/F1-2013-03-19 154412.csv\n./F1-FAILED/F3/F1-2013-03-19 170411.csv\n./F1-FAILED/F3/F1-2012-09-10 113409.csv\n./F1-FAILED/F2/F1-2011-10-03 113911.csv\n./F1-FAILED/F2/F1-2011-10-02 165908.csv\n./F1-FAILED/F2/F1-2011-10-02 212910.csv\n./F1-ARCHIVE/F1-2012-06-30 004408.csv\n./F1-ARCHIVE/F1-2011-08-17 133905.csv\n./F1-ARCHIVE/F1-2012-10-21 154410.csv\n./F1-ARCHIVE/F1-2012-05-08 190408.csv: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors
    
por Ubuntuser 10.09.2013 / 11:33

9 respostas

32

Usar for com find é a abordagem errada aqui, veja por exemplo this writeup sobre a lata de worms que você está abrindo.

A abordagem recomendada é usar find , while e read conforme descrito aqui . Abaixo está um exemplo que deve funcionar para você:

find . -type f -name '*.*' -print0 | 
while IFS= read -r -d '' file; do
    printf '%s\n' "$file"
done

Desta forma, você delimita os nomes dos arquivos com caracteres nulos ( find ), o que significa que a variação no espaço e outros caracteres especiais não causam problemas.

Para atualizar um arquivo com os arquivos que tar localiza, você pode passar a saída diretamente para tar :

find . -type f -name '*.*' -printf '%p
find . -type f -name '*.*' -print0 | 
while IFS= read -r -d '' file; do
    printf '%s\n' "$file"
done
' | tar --null -uf archive.tar -T -

Note que você não precisa diferenciar se o arquivo existe ou não, -printf irá lidar com isso de maneira sensata. Observe também o uso de ./ aqui para evitar incluir o %code% no arquivo.

    
por Thor 10.09.2013 / 12:38
11

Tente citar o loop for da seguinte forma:

for FILE in "'find . -type f  -name '*.*''"   # note the quotation marks

Sem aspas, o bash não lida bem com espaços e novas linhas ( \n ) ...

Tente também definir

IFS=$'\n'
    
por kiri 10.09.2013 / 11:38
6

Isso funciona e é mais simples:

find . -name '<pattern>' | while read LINE; do echo "$LINE" ; done

Credite a Rupa ( link ) por esta resposta.

    
por ShawnMilo 17.07.2014 / 23:11
4

Além da cotação adequada, você pode dizer a find para usar um separador NULL e ler e processar os resultados em um while loop

while read -rd $'
   -print0
          True; print the full file name on the standard output, followed by a null character (instead of the newline character that  -print  uses).   This  allows  file
          names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output.  This option corresponds to the
          -0 option of xargs.
' file; do something with "$file" done < <(find . -type f -name '*.*' -print0)

Isso deve tratar qualquer nome de arquivo que seja compatível com POSIX - veja man find

while read -rd $'
   -print0
          True; print the full file name on the standard output, followed by a null character (instead of the newline character that  -print  uses).   This  allows  file
          names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output.  This option corresponds to the
          -0 option of xargs.
' file; do something with "$file" done < <(find . -type f -name '*.*' -print0)
    
por steeldriver 10.09.2013 / 14:14
1
find . <find arguments> -print0 | xargs -0 grep <pattern>
    
por user2802945 27.05.2015 / 23:32
1

Eu fiz algo assim para encontrar arquivos que podem conter espaços.

IFS=$'\n'
for FILE in '/usr/bin/find $DST/shared -name *.nsf | grep -v bookmark.nsf | grep -v names.nsf'; do
    file $FILE | tee -a $LOG
done

Trabalhou como um encanto:)

    
por Scott B 18.05.2016 / 16:15
0

A maioria das respostas aqui quebram se houver um caractere de nova linha no nome do arquivo. Eu uso o bash mais de 15 anos, mas apenas interativo.

Em Python, você pode nos os.walk (): link

E o módulo tarfile: link

    
por guettli 10.09.2013 / 13:20
0

Acho que você pode estar melhor usando a opção -exec de find .

find . -type f -name '*.*' -exec tar -cpf archive.tar {} +

Localizar, em seguida, executa o comando usando uma chamada de sistema, para que os espaços e as novas linhas sejam preservados (em vez disso, um canal, o que exigiria a citação de caracteres especiais). Note que "tar -c" funciona quer o arquivo já exista ou não, e que (pelo menos com o bash) nem {} nem + precisam ser citados.

    
por Drake Clarris 10.09.2013 / 14:02
-1

Como sugerido por minerz029, é necessário citar a expansão do comando find . Você também precisa citar todas as substituições de $FILE em seu loop.

for FILE in "$(find . -type f  -name '*.*')"
do
    if [ ! -f archive.tar ]; then
        tar -cpf archive.tar "$FILE"
    else 
        tar -upf archive.tar "$FILE" 
    fi
done

Observe que a sintaxe $() deve ser preferida ao uso de backticks; veja este U & L pergunta . Eu também removi a palavra-chave [[ e a substitui pelo comando [ porque é POSIX.

    
por Joseph R. 10.09.2013 / 11:55