Enumere todos los directorios que NO tienen un archivo con un nombre de archivo dado dentro

11

¿Cómo entraría en la lista de todos los directorios que no tienen un archivo con un nombre de archivo dado? Por ejemplo, dado este árbol

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

Quiero enumerar los directorios que no tienen un archivo llamado README, en este caso sería directorio /c. ¿Como podría hacerlo? No puedo pensar en ninguna sintaxis usando eg find.

Renan
fuente
Es una pena que no hayas buscado: askubuntu.com/questions/196960/…
slm
No pensé en las palabras clave correctas al buscar, probablemente.
Renan
2
Solo estoy buscándote. He estado allí muchas veces donde no podía pensar en la palabra correcta para buscar algo 8-).
slm
relacionado: askubuntu.com/questions/196960/…
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

Respuestas:

5

Asumiendo una findimplementación como GNU findque acepte una {}incrustación en un argumento para -exec:

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

Ejemplo

Aquí los directorios 1/1 a 5/5 tienen un archivo README, los otros directorios están vacíos.

$ 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

Ahora cuando ejecutamos esta versión de nuestro findcomando:

$ 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

Referencias

slm
fuente
Cómo modificar el comando para buscar subdirectorios que no tengan una extensión de archivo específica (por ejemplo, * .txt). Modificar README con * .txt no parece funcionar
WanderingMind
@WanderingMind: si tiene una nueva pregunta, hágalo como nueva en el sitio ;-)
slm
3

Puede usar la -execopción de findverificar el archivo y luego imprimir todos los resultados para los cuales falla la verificación.

find /path/to/base -mindepth 1 -maxdepth 1 -type d -exec test -e {}/README \; -o -print
Patricio
fuente
3

No hay necesidad de find. Solo usa el shell:

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

Si necesita que sea recursivo, puede usar (para bash, zshpuede hacer esto por defecto, usar set -o globstaren ksh93):

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

(tenga en cuenta que los archivos de puntos están excluidos de forma predeterminada).

terdon
fuente
2

Con zshy calificadores glob ( ecadena ):

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

o

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

agregar Dpara incluir directorios ocultos:

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

/selecciona solo directorios y e_'[[ ! -f $REPLY/README ]]'_además selecciona solo los nombres de directorio para los que devuelve el código de shell entre las comillas true, es decir, para cada nombre de directorio ( $REPLY) al que se *(/)expande el globo , se ejecuta [[ ! -f $REPLY/README ]]y mantiene el nombre del directorio si el resultado es true.
La segunda forma ^e_'.....'_usa el mismo calificador global, negado (pero esta vez la expresión condicional no se niega:) [[ -f $REPLY/README ]].


Lo anterior solo devolverá los nombres de directorio en el directorio actual.
Si desea buscar de forma recursiva (nuevamente, para incluir directorios ocultos, agregue el Dcalificador):

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

Portablemente, podrías hacer:

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

[ -f file ]prueba si el archivo existe y se confirma que es un archivo normal (después de la resolución del enlace simbólico).

Si desea probar que solo existe (como una entrada en ese directorio), independientemente de su tipo, necesitaría: [ -e file ] || [ -L file ]aunque tenga en cuenta que necesita permiso de búsqueda en el directorio para realizar esas pruebas. Es posible que desee agregar algunas [ -x "$dir" ]pruebas para tener en cuenta esos casos como:

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 '{}' +

O para evitar la condición de carrera, con 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 '{}' +

Consulte también ¿Cómo puedo saber si un archivo normal no existe en Bash? en lo.

Stéphane Chazelas
fuente