¿Cómo enumero todos los archivos en un directorio excepto aquellos con extensiones especificadas?

28

Supongamos que tengo una carpeta que contiene .txt , .pdf y otros archivos. Me gustaría enumerar los "otros" archivos (es decir, archivos que no tienen las extensiones .txt o .pdf ). ¿Tienes algún consejo sobre cómo hacer esto?

Sé cómo enumerar archivos que no tienen una extensión determinada. Por ejemplo, si quiero enumerar todos los archivos excepto los archivos .txt , entonces

find -not -iname "*.txt"

o

ls | grep -v '\.txt$' | column

parece funcionar Pero, ¿cómo puedo enumerar todo excepto los archivos .txt o .pdf ? Parece que necesito usar algún tipo de "o" lógico en findo grep.

Andrés
fuente
2
Tenga en cuenta que el comportamiento de lsvs findvs globbing puede diferir para los archivos de puntos ocultos.
jw013
1
Otra cosa a tener en cuenta: findatravesará subdirectorios, como un recursivo ls. Use -maxdepth 1con findpara que se comporte más ls.
jw013
Entonces, no recursivo, ¿solo enumera los archivos en el directorio actual?
Margarita

Respuestas:

28

Suponiendo que uno tenga una versión apropiada de ls, esta es posiblemente la forma más simple:

ls -I "*.txt" -I "*.pdf"

Si desea recorrer en iteración todos los subdirectorios:

ls -I "*.txt" -I "*.pdf" -R
Dejian
fuente
8
Las lsopciones de GNU no son portátiles.
bahamat
44
lsde todos modos no pertenece realmente a los scripts portátiles, así que he estado asumiendo que el OP solo pregunta sobre el uso interactivo.
jw013
1
¿Por qué ls no es portátil? ¿Qué debo usar entonces?
Freedo
28

Encuentra apoyos -o

find . ! '(' -name '*.txt' -o -name '*.pdf' ')'

Necesita el paréntesis para que la precedencia sea correcta. Find hace muchas cosas; Sugiero leer a través de su página de manual.

También puede hacer un o en grep(pero realmente, no debe analizar la salida dels )

ls | grep -Ev '\.(txt|pdf)$' | column
derobert
fuente
1
¡Gracias! ¿Por qué no debería analizar la salida de ls?
Andrew
66
@Andrew primero, porque es frágil (considere un nombre de archivo con una nueva línea, sí, ese es un nombre de archivo válido, encuentre -print0 / -exec / -delete / etc. Evite ese problema); segundo, porque generalmente hay una manera más fácil.
derobert
Además de la página de findmanual, recomiendo de todo corazón los artículos sobre Unix Power Tools find, como docstore.mik.ua/orelly/unix3/upt/ch09_06.htm y docstore.mik.ua/orelly/unix3/upt/ch09_12.htm
Comodín
Solo para ayudar a decodificar la declaración para los humanos:find . NOT ( *.txt OR *.pdf )
wisbucky
12

Con bashglobbing extendido (activar con shopt -s extglob), el glob !(*.txt|*.pdf)debería funcionar. Puede pasar este globo directamente a cualquier comando que tome argumentos de archivo, incluidos, entre otros ls.

jw013
fuente
1
+1 pero si tiene subdirectorios, su contenido también aparecerá en la lista; use -dpara evitar eso:ls -d !(*.txt|*.pdf)
legends2k
Gracias, estaba buscando exactamente la sintaxis para aceptar varios archivos, esto encajaría bien aquí stackoverflow.com/questions/216995/…
Freedo
6

En zshcon extendedglob:

print -rl -- *~*.(txt|pdf)

o

print -rl -- ^*.(txt|pdf)

O con kshglob(sí, eso es ksh globbing no "bash extended globbing"):

print -rl -- !(*.txt|*.pdf)

Sin embargo, recuerde que también excluyen los archivos de puntos.

ksh93 tiene la característica FIGNORE(mis):

FIGNORE='@(.|..|*.txt|*.pdf)'
printf '%s\n' *
Thor
fuente
4
find /path/to/directory '!' -name '*.pdf' '!' -name '*.txt'

Esto es equivalente al comando con el operador OR, debido a las leyes de De Morgan .

Francesco Turco
fuente
3

Según lo sugerido por derobert, su mejor opción es usar find. Sin embargo, de hecho , puede utilizar el resultado en una tubería con otros comandos.

GNU (y algunos BSD) findadmiten el -print0predicado que le dice que imprima el nombre de archivo terminado por un carácter NUL , cuyo carácter no está permitido dentro de un nombre de archivo y garantiza que no habrá una colisión. Se pueden indicar otros comandos para usar el NUL como su delimitador de entrada.

El más importante de los cuales es GNU xargs, que ejecuta el comando que especifique y le pasa la lista de archivos como argumentos de línea de comando. Desea ejecutar xargs -r0conjuntamente con find's -print0Por ejemplo:

find . -type f ! \( -name \*.pdf -o -name \*.txt \) -print0 | xargs -r0 ls -ld

Esto imprime de forma segura una lista larga de todos los archivos pdf y txt , incluidos aquellos con espacios o caracteres no imprimibles en el nombre.

También puede usarlo con GNU de la tarsiguiente manera:

tar -zcf myarchive.tar.gz --null --files-from <(
  find . -type f ! -name \*.tar.gz -print0)

Esto crea un archivo tar.gz de todos los archivos cuyos nombres no terminan en .tar.gz

rsynctambién acepta archivos delimitados por nulos con el -0parámetro, al igual que muchos otros. Pero xargses el pegamento que usualmente usarás para este tipo de propósito. O eso o findla -execcaracterística.

tylerl
fuente
El tarejemplo de comando es bueno. Sin embargo, para la mayoría de los propósitos, -execse ajusta mejor.
Comodín
1

Si no tienes un subdirectorio

ls !(*.pdf|*.txt)

también debería funcionar!

Pero

ls -I "*.pdf" -I "*.txt"

Es la forma común.

abu_bua
fuente
0

Como complemento, si usa un shell compatible con bash, puede usar la variable GLOBIGNORE para excluir los resultados de la coincidencia de patrones. Del hombre:

   GLOBIGNORE
          A colon-separated list of patterns defining the set of filenames
          to be ignored by pathname expansion.  If a filename matched by a
          pathname  expansion  pattern also matches one of the patterns in
          GLOBIGNORE, it is removed from the list of matches.

En tu caso particular:

sh$ (GLOBIGNORE='*.pdf:*.txt'; ls -d *)

Tenga en cuenta que ejecuto ese comando como un sub-shell (usando paréntesis) para no alterar la variable de entorno GLOBIGNORE de mi shell interactivo.

Sylvain Leroux
fuente