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.