Erro no script. O comando find está pulando pastas com caracteres de espaço

Erro no script. O comando find está pulando pastas com caracteres de espaço

Preciso de um script que conte os arquivos em um diretório (e sub). Peguei o script seguido e mudei de acordo com minha necessidade.

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

Informações adicionais

  • Linux 2.6.22.19-0.4-default (este servidor não está mais em um ambiente produtivo).
  • GNU encontra 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.

Roteiro

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

Responder1

Estão faltando algumas aspas duplas (sempre coloque aspas duplas em substituições de variáveis $foo​​e substituições de comandos$(foo), a menos que você saiba por que pode deixá-los desligados com segurança e precisa deixá-los desligados). Mas esse não é todo o problema.

if [ ! -d $START ]

deveria estar if [ ! -d "$START" ].

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

Neste ponto, DIRScontém o nome do diretório inicial e seus subdiretórios recursivamente, com novas linhas entre eles. Portanto, se você tiver 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ê souber que não há novas linhas nos nomes dos arquivos, poderá analisar a saída de find, mas como saberia?

A propósito, não há problema em não colocar aspas duplas por $(…)aqui porque se trata de atribuições de variáveis, e as substituições nas atribuições são implicitamente protegidas. No entanto, observe que export DIRS=$(…)não é protegido de forma semelhante. É melhor usar aspas, a menos que você seja fluente em scripts de shell, assim como todas as pessoas que manterão seu script.

for d in $DIRS

É aqui que você perde: você quer dividir $DIRSem palavras, então não pode colocar aspas duplas, mas precisa de aspas duplas porque $DIRStodos os elementos estão concatenados e os nomes dos arquivos dentro dos espaços seriam separadores se você os deixasse sem aspas.


Normalmente, ao usar find, você deve fazer com que ele invoque o comando de processamento, com a -execopção. A menos que você tenha controles rígidos sobre os nomes dos arquivos, não analise a saída de find: é ambígua.

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 findcomando incorporado que se você analisar a saída de find, sua contagem será desativada se algum nome de arquivo contiver uma nova linha.

Responder2

Que tal apenas 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 script e funciona para diretórios com espaços, bem como outros caracteres não alfanuméricos.

Responder3

Você cometeu um erro clássico de citação. Corrija o loop for para ficar assim:

for d in "$DIRS"

Alternativamente, você pode alimentar finda saída diretamente, por exemplo:

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

Além disso, o || :bit é completamente redundante, pois echosempre tem valor de retorno 0.

informação relacionada