Wyświetl listę wszystkich katalogów, które NIE zawierają pliku o podanej nazwie

12

Jak powinienem wyświetlić listę wszystkich katalogów, w których nie ma pliku o podanej nazwie? np. biorąc pod uwagę to drzewo

/
  /a
     README
     file001
     file002
  /b
     README
     file001
  /c
     file003

Chcę wymienić katalogi, które nie mają nazwanych plików README, w tym przypadku byłby to katalog /c. Jak mam to zrobić? Nie mogę wymyślić żadnej składni używającej np find.

Renan
źródło
Wstyd, och, wstyd, że nawet nie szukałeś
slm
Prawdopodobnie nie szukałem odpowiednich słów kluczowych podczas wyszukiwania.
Renan
2
Po prostu walę cię w kotlety. Byłem tam wiele razy, w których nie mogłem znaleźć odpowiedniego słowa, aby wyszukać coś 8-).
slm
powiązane: askubuntu.com/questions/196960/…
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

Odpowiedzi:

5

Zakładając findimplementację taką jak GNU, findktóra akceptuje {}osadzony w argumencie -exec:

$ find . -type d \! -exec test -e '{}/README' \; -print

Przykład

Tutaj katalogi od 1/1 do 5/5 mają CZYTNIK, pozostałe katalogi są puste.

$ tree 
.
|-- 1
|   `-- 1
|       `-- README
|-- 10
|   `-- 10
|-- 2
|   `-- 2
|       `-- README
|-- 3
|   `-- 3
|       `-- README
|-- 4
|   `-- 4
|       `-- README
|-- 5
|   `-- 5
|       `-- README
|-- 6
|   `-- 6
|-- 7
|   `-- 7
|-- 8
|   `-- 8
`-- 9
    `-- 9

Teraz, gdy uruchomimy tę wersję naszego findpolecenia:

$ find . -type d \! -exec test -e '{}/README' \; -print
.
./10
./10/10
./7
./7/7
./9
./9/9
./6
./6/6
./5
./8
./8/8
./4
./1
./3
./2

Bibliografia

slm
źródło
Jak zmodyfikować polecenie, aby wyszukać podkatalogi, które nie mają określonego rozszerzenia pliku (powiedzmy * .txt). Modyfikacja pliku README za pomocą * .txt nie działa
WanderingMind
@WanderingMind - jeśli masz nowe pytanie, zadaj je jako nowe na stronie ;-)
slm
3

Możesz użyć -execopcji findsprawdzenia pliku, a następnie wydrukować wszystkie wyniki, dla których sprawdzenie się nie powiedzie.

find /path/to/base -mindepth 1 -maxdepth 1 -type d -exec test -e {}/README \; -o -print
Patrick
źródło
3

Nie ma potrzeby find. Wystarczy użyć powłoki:

for d in */; do [ -f "$d"README ] || printf '%s\n' "$d"; done
c/

Jeśli jest to potrzebne, aby być rekurencyjny, można użyć (na bash, zshmożna to zrobić domyślnie używają set -o globstarw ksh93):

shopt -s globstar
for d in **/; do [ -f "$d"README ] || printf '%s\n' "$d"; done

(zwróć uwagę, że pliki kropek są domyślnie wyłączone).

terdon
źródło
2

Z kwalifikatoramizsh i glob ( eciąg ):

print -rl -- *(/e_'[[ ! -f $REPLY/README ]]'_)

lub

print -rl -- *(/^e_'[[ -f $REPLY/README ]]'_)

dodaj, Daby uwzględnić ukryte katalogi:

print -rl -- *(D/e_'[[ ! -f $REPLY/README ]]'_)

/wybiera tylko katalogi i e_'[[ ! -f $REPLY/README ]]'_dalej wybiera tylko nazwy katalogów, dla których zwraca kod powłoki między cudzysłowami true, to znaczy dla każdej nazwy katalogu ( $REPLY), do której *(/)rozwija się glob , uruchamia [[ ! -f $REPLY/README ]]i zachowuje nazwę katalogu, jeśli wynik jest true.
Druga forma ^e_'.....'_używa tego samego kwalifikatora globalnego, negowanego (ale tym razem wyrażenie warunkowe nie jest negowane:) [[ -f $REPLY/README ]].


Powyższe zwróci tylko nazwy katalogów w bieżącym katalogu.
Jeśli chcesz wyszukiwać rekurencyjnie (ponownie, aby uwzględnić ukryte katalogi, dodaj Dkwalifikator):

print -rl ./**/*(/e_'[[ ! -f $REPLY/README ]]'_)
don_crissti
źródło
2

Przenośnie możesz zrobić:

find . -type d -exec sh -c '
  for dir do
    [ -f "$dir/README" ] || printf "%s\n" "$dir"
  done' sh '{}' +

[ -f file ]sprawdza, czy plik istnieje i czy jest normalny (po rozpoznaniu dowiązania symbolicznego).

Jeśli chcesz przetestować, czy istnieje tylko (jako wpis w tym katalogu), niezależnie od jego typu, potrzebujesz: [ -e file ] || [ -L file ]chociaż pamiętaj, że do wykonania tych testów potrzebujesz uprawnień do wyszukiwania w katalogu. Możesz dodać kilka [ -x "$dir" ]testów, aby uwzględnić takie przypadki, jak:

find . -type d -exec sh -c '
  for dir do
    if [ -x "$dir" ]; then
      [ -f "$dir/README" ] || printf "%s\n" "$dir"
    else
      printf >&2 "Cannot tell for \"%s\"\n" "$dir"
    fi
  done' sh '{}' +

Lub, aby uniknąć wyścigu, stosując zsh:

find . -type d -exec zsh -c '
  zmodload zsh/system
  for dir do
    ERRNO=0
    if [ ! -f "$dir/README" ]; then
      if [ "$errnos[ERRNO]" = ENOENT ]; then
        printf "%s\n" "$dir"
      else
        syserror -p "ERROR: $dir/README: "
      fi
    fi
  done' zsh '{}' +

Zobacz także Jak sprawdzić, czy zwykły plik nie istnieje w Bash? Na tak.

Stéphane Chazelas
źródło