`[!]` (signo de exclamación entre paréntesis) comodín en bash

10

Me he encontrado con patrones globales y comodines, y es de particular interés para mí [!].

Esta construcción es similar a la [!]construcción, excepto que, en lugar de hacer coincidir cualquier carácter dentro de los corchetes, coincidirá con cualquier carácter, siempre que no aparezca entre el [y ].

rm myfile [!192]

Lo anterior, creo, eliminará cualquier / todos los archivos, excepto cualquiera / todos los archivos que tengan 192 en su nombre.

Sin embargo, me preocupa el uso adecuado de esto con una extensión de archivo y, en particular, múltiples condiciones.

¿Cuál sería la sintaxis adecuada en tal situación?

rm myfile [!.gif .csv. mp3] 

o

rm myfile [!.gif !.csv !.mp3]

Mi preocupación es que el período podría estar fuera de lugar, por lo que cualquier archivo con un .(¿cuál sería alguno de ellos seguramente?) Luego sería manipulado, cuando estoy tratando de causar la manipulación de archivos específicos.

Esta construcción es similar a la [ ]construcción, excepto que, en lugar de hacer coincidir cualquier carácter dentro de los corchetes, coincidirá con cualquier carácter, siempre que no aparezca entre el [y ].

(Citado de http://www.tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm )

Ahora; para mí, eso sugiere que un singular !es suficiente, y todos los valores allí incluidos están contenidos dentro del rango.

IronUhlan
fuente
55
como un consejo general, use ls my-patterno echo my-commandpara verificar si el bloqueo se realiza de la manera que desea, antes de correrrm
Wayne_Yux
Además, recuerde que muchos, si no la mayoría, los archivos no tienen extensiones. Las extensiones en los sistemas Linux suelen ser irrelevantes y no se utilizan para definir el tipo de archivo por la mayoría de los programas.
terdon
2
Tenga en cuenta que su primera cita está mal citada, ahora parece decir básicamente que [!]es similar pero no como[!]
ilkkachu
2
Vale la pena mencionar que ^tiene exactamente el mismo significado que !en este contexto, por lo tanto, [^a]excluye aexactamente como lo [!a]hace: si el primer carácter que sigue a [es a! o un ^ entonces cualquier carácter no encerrado coincide. ( man bash)
postre
3
rm myfile [!192]eliminará myfile, así como cualquier archivo de un solo carácter que no se llame 1, 9o 2.
Kevin

Respuestas:

16

Citando man 7 glob:

An  expression  "[!...]"  matches  a single character, namely any character
that is not matched by the expression obtained by removing the first '!' from it.
(Thus, "[!]a-]" matches any single character except ']', 'a' and '-'.)

El énfasis en la parte de un solo carácter aquí, []no se puede usar para cadenas enteras en Bash Pattern Matching.


bashLa expansión Brace se puede usar para unir cadenas, sin embargo, no hay forma (que yo sepa) de negarlas. P.ej

1.{gif,csv,mp3}

se expande a

1.gif 1.csv 1.mp3

no importa si existen estos archivos.


Como señaló wchargin , shoptse puede utilizar para habilitar operadores de coincidencia de patrones extendidos, uno de ellos es !(). Después de correr shopt -s extglob, p. Ej.

1.!(gif|csv|mp3)

se expande a

1.jpg 1.bmp 1.png

si esos archivos existen en el directorio actual. Para más información man bash(sección EXPANSIÓN / Expansión de nombre de ruta) y Expansión de nombre de ruta (globalización) .


Para lo que estás tratando de hacer, siempre usaría find, que ofrece la negación y mucho más, por ejemplo, de la siguiente manera:

find . -maxdepth 1 -type f ! -name "*.gif" -a ! -name "*.csv" -a ! -name "*.mp3"

Esto solo enumera los resultados, si desea eliminarlos simplemente agregue la -deleteopción al final del comando. Como siempre con las acciones de eliminación: siempre verifique cuidadosamente primero .

findofrece un montón de opciones útiles muy bien explicadas por man find.

postre
fuente
1
Tenga en cuenta que este findcomando es recursivo ( edición: como se mostró originalmente , podría guardar este comentario para la información adicional relevante pero no esencial que transmite). Para buscar archivos que residen solo en el directorio actual pero no también en sus subdirectorios, y, por lo tanto, solo elimine esos archivos si -deletese agrega la acción, puede agregarlos -maxdepth 1inmediatamente después find .. Esto lo alinearía con el efecto de globbing, ya que los globs no son recursivos en Bash a menos que la globstaropción de shell esté activada y **se use. El globo que se muestra en la pregunta no es recursivo en todos los depósitos.
Eliah Kagan
2
"no hay manera (no conozco) para negar los" -con shopt -s extglob, se puede utilizar echo 1.!(gif|csv|mp3), que imprime todo 1.ext, donde extno es gif, csvo mp3. (Consulte la subsección "Coincidencia de patrones" de man bash.)
wchargin
10

Creo que entendiste mal el uso de corchetes. [abc]coincide con uno de los caracteres a, bo c. [!abc]coincide con un carácter que no es a, bo c. Entonces el comando

rm myfile [192]

eliminará myfiley los archivos 1, 9y 2porque tiene un espacio entre myfile y el soporte. Por el contrario, el comando

rm myfile[192]

eliminará los archivos myfile1, myfile9y myfile2.

pLumo
fuente
cierto. Mejor de usar find.
pLumo
En realidad [] se usa para un rango, [!] Un solo personaje
IronUhlan
1
@IronUhlan, ambos toman un rango o una lista de personajes. Es solo que el otro es una negación.
ilkkachu
7

Los corchetes [ ]denotan una clase de personaje. Cualquier personaje individual dentro de ellos puede coincidir en la posición dada. Entonces

file[192]

coincide con tres nombres posibles:

file1
file2
file9

No , no coincide file192, ya que sólo se ocupa del carácter individual después file.

También podemos usar rangos entre paréntesis. [0-9]coincide con cualquier dígito en la posición dada. Para dos dígitos, necesitamos [0-9][0-9]. Para un dígito seguido de una letra mayúscula, podríamos usar [0-9][A-Z]...

! indica negación

file[!192]

coincidirá con archivos que tengan un solo carácter después de fileexcepto 1o 9o 2. Por ejemplo, se emparejará file7y filese file%(y muchos otros), pero no file192 , ya que tiene dos caracteres adicionales.

Para su caso de uso, sugiero usar en findlugar de [!]. Cuenta con un notoperador.

También es recursivo , lo que significa que entra en todos los subdirectorios de forma predeterminada. Puede limitarlo al directorio actual con -maxdepth 1, si eso es lo que desea. Los comodines de shell (globbing) no son recursivos (aunque **se puede habilitar el globbing recursivo con ).

Suponiendo que no desea evitar eliminar enlaces simbólicos, podemos agregar -type da las pruebas negadas para evitar eliminar cualquier directorio. Gracias a Eliah Kagan por esa sugerencia.

find /path -maxdepth 1 -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \)

Si ve lo que quiere, puede ejecutar el comando nuevamente con -delete

find /path -maxdepth 1  -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \) -delete
Zanna
fuente
2
Y creo que también puedes hacer varios rangos a la vez. Por ejemplo, el dígito hexadecimal podría ser[0-9a-fA-F]
Wilson
@Zanna, sí! En realidad estaba usando find :) No me di cuenta de que tenía un operador "no" :)
IronUhlan