Directory con due o più file


11

Voglio trovare una sottodirectory della directory corrente, che (quella è la sottodirectory) contiene 2 o più file regolari.

Non sono interessato alle directory che contengono meno di 2 file, né alle directory che contengono solo sottodirectory.

Risposte:


12

Ecco un approccio completamente diverso basato su GNU finde uniq. Questo è molto più veloce e molto intuitivo per la CPU rispetto alle risposte basate sull'esecuzione di un comando shell che conta i file per ogni directory trovata.

find . -type f -printf '%h\n' | sort | uniq -d

Il findcomando stampa la directory di tutti i file nella gerarchia e uniqvisualizza solo le directory che appaiono almeno due volte.


2
Non dovresti analizzare l'output di find. In questo caso, poiché GNU findmanipolerà i nomi delle directory che hanno caratteri che non sono stampabili nella locale corrente (come "ä" nella locale C). Vedi anche unix.stackexchange.com/questions/321697/…
Kusalananda

4
@Kusalananda, non quando l'output non arriva a un valore. Qui, l'unico problema è con i personaggi newline, che puoi risolvere usando-printf '%h\0' | sort -z | uniq -zd | xargs -r0 ...
Stéphane Chazelas,

6
find . -type d \
    -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' \
    -print

Questo troverà tutti i nomi dentro o sotto la directory corrente e quindi filtrerà tutti i nomi che non sono nomi di directory.

I nomi di directory rimanenti verranno assegnati a questo breve script:

c=0
for n in "$1"/*; do
    [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 ))
done

[ "$c" -ge 2 ]

Questo script conterà il numero di file regolari (saltando i collegamenti simbolici) nella directory indicata come primo argomento della riga di comando (da find). L'ultimo comando nello script è un test per vedere se il conteggio era 2 o maggiore. Il risultato di questo test è il valore restituito (stato di uscita) dello script.

Se il test ha esito positivo, -printverrà findstampato il percorso della directory.

Per considerare anche i file nascosti (file il cui nome inizia con un punto), cambia lo sh -cscript dal dire

for n in "$1"/*; do

per

for n in "$1"/* "$1"/.*; do

test:

$ tree
.
`-- test
    |-- a
    |-- dir1
    |   |-- a
    |   |-- b
    |   `-- c
    `-- dir2
        |-- dira
        |-- dirb
        |   |-- file-1
        |   `-- file-2
        `-- dirc

6 directories, 6 files

$ find . -type d -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' -print
./test/dir1
./test/dir2/dirb

La tua soluzione non conta i file con un nome che inizia con un punto. È inoltre necessario inizializzare c = 0 per evitare messaggi di errore con directory che non contengono alcun file.
Xhienne,

@xhienne Ho considerato i file nascosti e aggiungerò una nota a riguardo. Non ci sono errori se non ci sono file regolari in una directory poiché [ "" -ge 2 ]è un test valido.
Kusalananda

Non sono sicuro di come definisci "valido". POSIX richiede che arg1 sia un valore intero. dash, bash --posixE testtutte le visualizzazione di un messaggio di errore ed esce con 2 (vale a dire "l'errore è verificato un")
xhienne

@xhienne Ah, stavo testando un sistema che kshfunziona come sh. Si modificherà immediatamente. Grazie per avermi beccato! :-)
Kusalananda

Inoltre, [ -f ... ]dereferenze collegamenti simbolici. È necessario aggiungere un test per eliminarli poiché la domanda specifica che devono essere conteggiati solo i file regolari.
Xhienne,

6

Con l'aiuto della risposta di Gilles su SU e il suo rovescio e alcune modifiche, ecco cosa ti serve.

find . -type d -exec sh -c 'set -- "$1"/*;X=0; 
    for args; do [ -f "$args" ] && X=$((X+1)) ;done; [ "$X" -gt 1 ] ' _ {} \; -print

Albero delle directory.

.
├── test
│   ├── dir1
│   │   ├── a
│   │   ├── b
│   │   └── c
│   ├── dir2
│   │   ├── dira
│   │   │   └── a file\012with\012multiple\012line
│   │   ├── dirb
│   │   │   ├── file-1
│   │   │   └── file-2
│   │   └── dirc
│   ├── diraa
│   ├── dirbb
│   ├── dircc
│   └── x
│   └── x1
│   └── x2
└── test2
    ├── dir3
    └── dir4

Risultato:

./test
./test/dir1
./test/dir2/dirb

Ho avuto anche questo all'inizio, ma avrai problemi con le directory che contengono più sottodirectory e file. Inoltre non elimina le directory che contengono solo le sottodirectory.
Kusalananda

Non lo risolve davvero. Trova sia la directory che testla dir2directory nella mia configurazione di test (vedi la mia risposta).
Kusalananda

Funziona per il tuo esempio, ma aggiungi test/x1e anche test/x2come file ... $1e $2saranno directory per test, e la directory ci mancherà.
Kusalananda

@Kusalananda In nessun modo ho trovato, tranne ciò che hai risposto, ho cercato di modificare una parte del mio comando per non essere il tuo duplicato esatto (non ho escluso i file nascosti come hai fatto tu), mi scuso.
αғsнιη,

1
Nessun
problema

3

Un altro approccio find+ wc:

find path/currdir -maxdepth 1 -type d ! -empty ! -path "path/currdir" \
-exec sh -c 'count=$(find "$1" -maxdepth 1 -type f | wc -l); [ $count -ge 2 ]' _ {} \; -print

  • path/currdir - percorso alla directory corrente

  • -maxdepth 1- considera solo le sottocartelle figlio dirette

  • ! -empty - ignora le sottocartelle vuote

  • ! -path "path/currdir" - ignora il percorso della directory corrente

  • count=$(find "$1" -maxdepth 1 -type f | wc -l)- countè assegnato con il numero di file per ogni sottocartella trovata

  • [ $count -ge 2 ] ... -print - stampa il nome / percorso della sottocartella contenente 2 o più file regolari

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.