script programas programar programacion pasar parametros manejo ejemplos comandos comando cadenas bash shell pattern-matching glob

bash - programas - ¿Cómo puedo usar comodines inversos o negativos cuando coincida un patrón en un shell de unix/linux?



programacion shell linux ejemplos (11)

El siguiente trabajo enumera todos los archivos *.txt en el directorio actual, excepto aquellos que comienzan con un número.

Esto funciona en bash , dash , zsh y todos los demás shells compatibles con POSIX.

for FILE in /some/dir/*.txt; do # for each *.txt file case "${FILE##*/}" in # if file basename... [0-9]*) continue ;; # starts with digit: skip esac ## otherwise, do stuff with $FILE here done

  1. En la línea uno, el patrón /some/dir/*.txt hará que el bucle for /some/dir/*.txt en todos los archivos de /some/dir cuyo nombre termine con .txt .

  2. En la línea dos se usa una declaración de caso para eliminar archivos no deseados. - La expresión ${FILE##*/} elimina cualquier componente del nombre de directorio principal del nombre de archivo (aquí /some/dir/ ) para que los patrones puedan coincidir solo con el nombre base del archivo. (Si solo está eliminando nombres de archivos basados ​​en sufijos, puede acortar esto a $FILE ).

  3. En la línea tres, todos los archivos que coincidan con el patrón de case [0-9]* ) se omitirán (la instrucción continue salta a la siguiente iteración del bucle for ). - Si lo desea, puede hacer algo más interesante aquí, por ejemplo, omitir todos los archivos que no comienzan con una letra (a – z) usando [!az]* , o puede usar varios patrones para omitir varios tipos de nombres de archivos, por ejemplo [0-9]*|*.bak para omitir tanto los archivos .bak como los archivos que no comienzan con un número.

Digamos que quiero copiar el contenido de un directorio excluyendo archivos y carpetas cuyos nombres contengan la palabra ''Música''.

cp [exclude-matches] *Music* /target_directory

¿Qué debe ir en lugar de [excluir coincidencias] para lograr esto?


En Bash puedes hacerlo habilitando la opción extglob , como esta (reemplaza ls con cp y agrega el directorio de destino, por supuesto)

~/foobar> shopt -s extglob ~/foobar> ls abar afoo bbar bfoo ~/foobar> ls !(b*) -bash: !: event not found ~/foobar> shopt -s extglob # Enables extglob ~/foobar> ls !(b*) abar afoo ~/foobar> ls !(a*) bbar bfoo ~/foobar> ls !(*foo) abar bbar

Más tarde puede deshabilitar extglob con

shopt -u extglob


En bash, una alternativa a shopt -s extglob es la variable GLOBIGNORE . No es realmente mejor, pero me resulta más fácil de recordar.

Un ejemplo que puede ser lo que quería el cartel original:

GLOBIGNORE="*techno*"; cp *Music* /only_good_music/

Cuando unset GLOBIGNORE , unset GLOBIGNORE para poder rm *techno* en el directorio de origen.


Esto lo haría excluyendo exactamente ''Música''.

cp -a ^''Music'' /target

esto y aquello para excluir cosas como música? * o *? música

cp -a ^/*?''complete'' /target cp -a ^''complete''?/* /target


La opción de shell extglob le brinda una coincidencia de patrones más poderosa en la línea de comandos.

Lo enciende con shopt -s extglob y lo desactiva con shopt -u extglob .

En tu ejemplo, inicialmente harías:

$ shopt -s extglob $ cp !(*Music*) /target_directory

Los operadores de glob bing completos y de extremo ext. Disponibles son (extracto de man bash ):

Si la opción de shell extglob está habilitada utilizando el componente shopt, se reconocen varios operadores de coincidencia de patrones extendidos. En la siguiente descripción, una lista de patrones es una lista de uno o más patrones separados por un |. Los patrones compuestos se pueden formar utilizando uno o más de los siguientes sub-patrones:

  • ? (Lista de patrones)
    Coincide con cero o una aparición de los patrones dados
  • * (Lista de patrones)
    Coincide con cero o más apariciones de los patrones dados
  • + (lista de patrones)
    Coincide con una o más apariciones de los patrones dados
  • @ (lista de patrones)
    Coincide con uno de los patrones dados
  • ! (Lista de patrones)
    Coincide con cualquier cosa excepto uno de los patrones dados

Entonces, por ejemplo, si quisiera enumerar todos los archivos en el directorio actual que no son archivos .c o .h , haría:

$ ls -d !(*@(.c|.h))

Por supuesto, el funcionamiento normal del shell shelling, por lo que el último ejemplo también podría escribirse como:

$ ls -d !(*.[ch])


Mi preferencia personal es usar grep y el comando while. Esto le permite a uno escribir scripts potentes pero legibles, asegurándose de que termine haciendo exactamente lo que quiere. Además, al utilizar un comando de eco, puede realizar una ejecución en seco antes de llevar a cabo la operación real. Por ejemplo:

ls | grep -v "Music" | while read filename do echo $filename done

Imprimirá los archivos que terminará copiando. Si la lista es correcta, el siguiente paso es simplemente reemplazar el comando echo con el comando copy de la siguiente manera:

ls | grep -v "Music" | while read filename do cp "$filename" /target_directory done


No en bash (que yo sepa), sino en:

cp `ls | grep -v Music` /target_directory

Sé que esto no es exactamente lo que buscabas, pero resolverá tu ejemplo.


Si desea evitar el costo de mem de usar el comando exec, creo que puede hacerlo mejor con xargs. Creo que la siguiente es una alternativa más eficiente para

find foo -type f ! -name ''*Music*'' -exec cp {} bar /; # new proc for each exec find . -maxdepth 1 -name ''*Music*'' -prune -o -print0 | xargs -0 -i cp {} dest/


También puedes usar un bucle bastante simple for :

for f in `find . -not -name "*Music*"` do cp $f /target/dir done


Un truco que todavía no he visto aquí que no usa extglob , find o grep es tratar dos listas de archivos como conjuntos y "dif" usarlas mediante comm :

comm -23 <(ls) <(ls *Music*)

comm es preferible sobre diff porque no tiene crucero adicional.

Esto devuelve todos los elementos del conjunto 1, ls , que no están también en el conjunto 2, ls *Music* . Esto requiere que ambos conjuntos estén ordenados para funcionar correctamente. No hay problema para ls y glob expansion, pero si está usando algo como find , asegúrese de invocar el sort .

comm -23 <(find . | sort) <(find . | grep -i ''.jpg'' | sort)

Potencialmente útil.


Una solución para esto se puede encontrar con encontrar.

$ mkdir foo bar $ touch foo/a.txt foo/Music.txt $ find foo -type f ! -name ''*Music*'' -exec cp {} bar /; $ ls bar a.txt

Find tiene bastantes opciones, puede ser bastante específico sobre lo que incluye y excluye.

Edit: Adam en los comentarios señaló que esto es recursivo. las opciones de búsqueda mindepth y maxdepth pueden ser útiles para controlar esto.