expansión de shell (A | B) en los nombres de archivo?

9

¿Es posible expandir una oropción en el shell al leer un archivo, por ejemplo.

Lo que quiero decir con esto es que, por ejemplo, grepadmite sintaxis como (A|B)para que coincida con A o B en un archivo.

Del mismo modo, si tengo estos archivos:

file1.txt
file2.txt
file3.txt
file4.txt
file5.txt

Lo que podía hacer cat file{1..5}.txten el bash, ya que amplía la gama. ¿Hay una forma equivalente de hacer esto solo con un par de archivos?

Por ejemplo, cat file(1|5).txty solo imprimir esos 2?

Joe Healey
fuente

Respuestas:

16

El patrón estándar de nombre de archivo para igualar un dígito es [0-9]. Esto coincide con un solo dígito:

cat file[0-9].txt

Para seleccionar solo dos de estos:

cat file[25].txt

Para números mayores que 9, la expansión de llaves será útil (pero vea la nota a continuación para ver la diferencia entre los patrones globales y las expansiones de llaves):

cat file{25..60}.txt

Nuevamente, la expansión de llaves también permite números individuales:

cat file{12,45,900,xyz}.txt

(tenga en cuenta que en el ejemplo anterior, la expansión de llaves no implica un ciclo aritmético, sino que solo genera nombres basados ​​en las cadenas proporcionadas).

En bash, con la extglobopción de shell habilitada ( shopt -s extglob), lo siguiente también funcionará:

cat file@(12|45|490|foo).txt

El @(...)patrón coincidirá con cualquiera de los |patrones delimitados incluidos .


La diferencia entre los patrones de englobamiento como [...]y @(...)y expansiones Brace, es que una expansión de llaves se genera en la línea de comandos y en realidad no puede coincidir con los nombres existentes en el directorio actual. Un patrón global de nombre de archivo coincidirá con los nombres, pero el shell no se quejará si no existen todos los nombres posibles. Si no existe un nombre coincidente, el patrón permanecerá sin expandir, a menos que también se establezca la nullglobopción de shell, en cuyo caso se elimina el patrón.

Ejemplo:

touch file1

ls file[0-9]

Aquí, solo file1se mostrará la lista de archivos para .

Con ls file{0..9}, lsse quejaría de no encontrar file0, file2etc.

En el siguiente ejemplo, el primer comando solo tocará los nombres existentes que coincidan con el patrón dado, mientras que la segunda línea creará archivos que aún no existen:

touch file[0-9]

touch file{0..9}
Kusalananda
fuente
@thecarpy lo siento, pero ese no es un patrón que coincida, por ejemplo file45.txt. La expresión de corchete [...]funciona igual que en la expresión regular, pero se usa !en lugar de ^decir "no en". Un [...]patrón siempre coincidirá con un solo carácter.
Kusalananda
¡Tienes razón! Por cierto, mi {1,2}tampoco es compatible con POSIX ... ¡aprendí algunas cosas nuevas hoy!
thecarpy
2
El hecho de que las expansiones de llaves no necesiten coincidir con archivos reales puede ser extremadamente útil, si lo está utilizando para (por ejemplo) generar patrones para pasar grep, generar URL para pasar curl, etc., pero también puede ser confuso a personas acostumbradas a trabajar con globos.
Kevin
@Kevin Correcto. Una expansión de llaves no necesita ser sobre nombres de archivo.
Kusalananda
7

La sintaxis a usar es la file{1,2}que evaluará a file1y file2.

$ ls
$ touch file{1,2,3,4,5,6,7,8,9}
$ ls
file1  file2  file3  file4  file5  file6  file7  file8  file9

Como también señaló Inian a continuación ... sería más fácil hacerlo touch file{1..9}en este caso de ejemplo ...

$ ls
$ touch file{1..9}
$ ls
file1  file2  file3  file4  file5  file6  file7  file8  file9

También puede usar múltiples expresiones, como:

$ ls
$ touch file{1..9}{a..z}
$ ls
file1a file1b file1c
[...]
file9x file9y file9z

Sí, lo anterior creará 234 ( 9veces 26) archivos.

thecarpy
fuente
1
Esta file{1,2} sintaxis también es conveniente para cambiar el nombre de los archivos:mv some_very_long_filename.txt{,.bak}
Eric Duminil
6

Sí, puede usar la expansión de llaves en el bashshell. Por solo un par de archivos, simplemente hazlo file{1..2}o simplementefile{1,2}

O si le preocupa que los archivos no estén allí en algunos casos, simplemente haga un bucle simple,

for file in file{1..4}.txt; do
    [ -f "$file" ] || continue
    echo "$file" # Do other actions on file here
done

O bien, si su única operación en los archivos es concatenar y no está seguro de qué archivos no podrían estar presentes en ningún momento, solo catelimínelos. Redirigir el error estándar a /dev/nullsuprimiría los errores si el archivo no está disponible.

cat file{1,5}.txt 2>/dev/null

o use la expresión global file[15]que no se queja de los errores si no se encuentra el archivo.

cat file[15].txt
Inian
fuente
2
No, no me preocupa que los archivos no estén allí, solo quería mostrar el contenido de 2 archivos numerados numéricamente, pero eso no es consecutivo, ¡así que la file{1,5}sintaxis de coma era todo lo que me faltaba!
Joe Healey