Erro no script. Comando find está pulando pastas com caractere de espaço

1

Eu preciso de um script que conte os arquivos em um diretório (e sub). Eu peguei o roteiro seguido e mudei para a minha necessidade.

Funciona como deveria, exceto para pastas com caracteres de espaço. Tenho certeza que estou perdendo citações em qualquer lugar, mas ainda não consigo descobrir.

Informações adicionais

  • Linux 2.6.22.19-0.4-default (esse servidor não está mais em um ambiente produtivo).
  • GNU encontra a versão 4.2.31
  • Não consigo renomear os diretórios.

Exemplo de estrutura de diretório

.
..
01_infos
02_sent
03_inbox
04_public and private
197.
145.
329.
13.

Script

#!/bin/bash
# Write a script that will count the number of files in each of your subdirectories.
# -------------------------------------------------------------------------
# Copyright (c) 2001 nixCraft project <http://cyberciti.biz/fb/>
# This script is licensed under GNU GPL version 2.0 or above
# -------------------------------------------------------------------------
# This script is part of nixCraft shell script collection (NSSC)
# Visit http://bash.cyberciti.biz/ for more information.
# -------------------------------------------------------------------------

START=$HOME

# change your directory to command line if passed
# otherwise use home directory
[ $# -eq 1 ] && START=$1 || :

if [ ! -d $START ]
then
        echo "$START not a directory!"
        exit 1
fi

# use find command to get all subdirs name in DIRS variable
DIRS=$(find "$START" -type d)

# loop thought each dir to get the number of files in each of subdir
for d in $DIRS
do
        echo "$d directory has $(find "$d" -maxdepth 1 -regex '.*\.' -type f | wc -l) files" || :
done

saída

./01_infos directory has 1 files
./02_sent directory has 9 files
./03_inbox has 4 files
find: ./04_public: No such file or directory
    
por JohannesM 03.07.2012 / 20:41

3 respostas

1

Que tal exatamente isso?

find . -type d -exec sh -c '/bin/echo -n "{}"; find "{}" -maxdepth 1 -regex ".*\." -type f | wc -l; ' \;

A saída não é tão açucarada, mas não requer um script e funciona para diretórios com espaços, assim como outros caracteres não alfanuméricos.

    
por 03.07.2012 / 21:20
2

Você está perdendo algumas aspas duplas ( sempre coloca aspas duplas em torno das substituições de variáveis $foo e substituições de comandos $(foo) , a menos que você saiba por que você pode seguramente deixá-las fora e precisa deixá-las fora). Mas esse não é o problema todo.

if [ ! -d $START ]

deve ser if [ ! -d "$START" ] .

DIRS=$(find "$START" -type d)

Neste ponto, DIRS contém o nome do diretório inicial e seus subdiretórios recursivamente, com novas linhas no meio. Então, se você tem algum nome de diretório que contenha uma nova linha, você perdeu: é impossível saber quais novas linhas vieram de um nome de diretório e quais eram separadores. Se você sabe que não há novas linhas em nomes de arquivos, você pode analisar a saída de find , mas como você saberia?

A propósito, é ok não ter aspas duplas em torno de $(…) aqui, porque isso é uma atribuição de variáveis, e substituições em atribuições são implicitamente protegidas. No entanto, observe que export DIRS=$(…) não é protegido de maneira semelhante. É melhor usar aspas, a menos que você seja fluente em scripts de shell e também todas as pessoas que manterão seu script.

for d in $DIRS

Aqui é onde você perde: você quer dividir $DIRS em palavras, então você não pode colocar aspas duplas, mas você precisa de aspas duplas porque $DIRS tem todos os elementos concatenados juntos e os nomes dos arquivos dentro dos espaços separadores se você os deixar sem aspas.

Normalmente, quando você usa find , deve fazê-lo chamar o comando de processamento, com a opção -exec . A menos que você tenha controles rígidos sobre os nomes dos arquivos, não analise a saída de find : é ambíguo.

find "$START" -type d -exec sh -c '
    echo "$0 directory has $(find "$0" -maxdepth 1 -regex ".*\." -type f -printf \n | wc -l) files whose name ends with ."
' {} \;

Observe novamente no comando find incorporado que, se você analisar a saída de find , sua contagem será desativada se qualquer nome de arquivo contiver uma nova linha.

    
por 04.07.2012 / 02:28
-2

Você tem um erro clássico de citar. Corrigir o loop for para ficar assim:

for d in "$DIRS"

Como alternativa, você pode fornecer diretamente a saída de find , por exemplo:

find "$START" -type d | while read d
do # and so on...

Como um aparte, o || : bit é completamente redundante, já que echo sempre tem um valor de retorno de 0.

    
por 03.07.2012 / 20:58

Tags