Katalogi z dwoma lub więcej plikami

11

Chcę znaleźć podkatalog bieżącego katalogu, który (tj. Podkatalog) zawiera 2 lub więcej zwykłych plików.

Nie interesują mnie katalogi zawierające mniej niż 2 pliki, ani katalogi zawierające tylko podkatalogi.

porton
źródło

Odpowiedzi:

12

Oto zupełnie inne podejście oparte na GNU findi uniq. Jest to o wiele szybsze i bardziej przyjazne dla procesora niż odpowiedzi oparte na wykonywaniu polecenia powłoki, które zlicza pliki dla każdego znalezionego katalogu.

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

findKomenda drukuje katalog wszystkich plików w hierarchii i uniqwyświetla tylko te katalogi, które pojawiają się co najmniej dwa razy.

Xhienne
źródło
2
Nie powinieneś analizować wyniku find. W tym przypadku, ponieważ GNU findbędzie zmieniać nazwy katalogów, które mają znaki, których nie można wydrukować w bieżących ustawieniach narodowych (takich jak „ä” w ustawieniach regionalnych C). Zobacz także unix.stackexchange.com/questions/321697/…
Kusalananda
4
@Kusalananda, nie wtedy, gdy wynik nie przechodzi w tty. Tutaj jedynym problemem są znaki nowej linii, które można naprawić za pomocą-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

Spowoduje to znalezienie wszystkich nazw w bieżącym katalogu lub pod nim, a następnie odfiltrowanie wszystkich nazw, które nie są nazwami katalogów.

Pozostałe nazwy katalogów zostaną podane temu krótkiemu skryptowi:

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

[ "$c" -ge 2 ]

Ten skrypt policzy liczbę zwykłych plików (pomijając dowiązania symboliczne) w katalogu podanym jako pierwszy argument wiersza poleceń (od find). Ostatnie polecenie w skrypcie jest testem sprawdzającym, czy liczba wynosiła 2 lub więcej. Wynikiem tego testu jest wartość zwracana (status wyjścia) skryptu.

Jeśli test się powiedzie, -printspowoduje findwydrukowanie ścieżki do katalogu.

Aby uwzględnić również ukryte pliki (pliki, których nazwy zaczynają się od kropki), zmień sh -cskrypt w powiedzeniu

for n in "$1"/*; do

do

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

Testowanie:

$ 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
Kusalananda
źródło
Twoje rozwiązanie nie liczy plików o nazwie zaczynającej się od kropki. Powinieneś także zainicjować c = 0, aby uniknąć komunikatów o błędach w katalogach, które nie zawierają żadnego pliku.
xhienne,
@xhienne Rozważyłem ukryte pliki i dodam notatkę na ten temat. Nie ma błędu, jeśli w katalogu nie ma zwykłych plików, ponieważ [ "" -ge 2 ]jest to prawidłowy test.
Kusalananda
Nie wiesz, jak zdefiniujesz „prawidłowy”. POSIX wymaga, aby arg1 było liczbą całkowitą. dash, bash --posixi testwszystkie wyświetlają komunikat o błędzie i wychodzą z 2 (tzn. „Wystąpił błąd”)
xhienne
@xhienne Ah, testowałem na systemie, który kshdziała jako mas sh. Poprawi się natychmiast. Dzięki za szturchanie mnie! :-)
Kusalananda
Ponadto [ -f ... ]wyklucza dowiązania symboliczne. Powinieneś dodać test, aby je wyeliminować, ponieważ pytanie określa, że ​​należy liczyć tylko zwykłe pliki.
xhienne
6

Z pomocą odpowiedzi Gillesa na SU i jej odwrotnej kolejności oraz modyfikacji, oto czego potrzebujesz.

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

Drzewo katalogów.

.
├── 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

Wynik:

./test
./test/dir1
./test/dir2/dirb
αғsнιη
źródło
Miałem to też na początku, ale będziesz miał problem z katalogami zawierającymi wiele podkatalogów i plików. Nie usuwa też katalogów zawierających tylko podkatalogi.
Kusalananda
Tak naprawdę to nie rozwiązuje. Znajduje zarówno katalogi, jak testi dir2katalogi w mojej konfiguracji testowej (patrz moja odpowiedź).
Kusalananda
Działa na twoim przykładzie, ale dodaj test/x1i test/x2jako pliki również ... $1i $2będą katalogami dla test, a katalog zostanie pominięty.
Kusalananda
@Kusalananda Nie ma mowy, że nie znalazłem, poza tym, na co odpowiedziałeś, próbowałem zmienić część mojego polecenia, aby nie było dokładnym duplikatem twojego (nie wykluczyłem ukrytych plików, jak to zrobiłeś), przepraszam.
αғsнιη
1
Bez obaw :-)
Kusalananda
3

Inne podejście 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 - ścieżka do twojego bieżącego katalogu

  • -maxdepth 1- rozważ tylko bezpośrednie podfoldery podrzędne

  • ! -empty - ignoruj ​​puste podfoldery

  • ! -path "path/currdir" - zignoruj ​​bieżącą ścieżkę katalogu

  • count=$(find "$1" -maxdepth 1 -type f | wc -l)- countjest przypisany z liczbą plików dla każdego znalezionego podfolderu

  • [ $count -ge 2 ] ... -print - wydrukuj nazwę / ścieżkę podfolderu zawierającą 2 lub więcej zwykłych plików

Roman Perekhrest
źródło