Comodins a Bash: expansió de noms de fitxers | Iespai
Linux

Comodins a Bash: expansió de noms de fitxers i directoris a Linux



Quan treballes al terminal de Linux, sovint has de referir-te a molts fitxers de cop: totes les fotos, tots els .txt, els informes del 2024… Per això Bash té els comodins (wildcards o globs): caràcters especials que el shell substitueix per la llista de noms de fitxer que hi encaixen. En aquest post repassem els comodins bàsics, les expansions de la titlla ~ i de les claus {}, i els comodins estesos de Bash, amb exemples provats.


La clau: qui expandeix és el shell, no la comanda

Abans d’executar una comanda, Bash substitueix els comodins per la llista de fitxers que hi coincideixen i després executa la comanda amb aquesta llista. La comanda no veu mai l’asterisc. Ho pots comprovar amb echo, que és la millor manera de provar un patró abans de fer-lo servir amb rm o mv:

$ ls
carta10.txt  carta1.txt  carta2.txt  foto.jpg  informe.txt
$ echo *.txt
carta10.txt carta1.txt carta2.txt informe.txt

Quan escrius ls *.txt, en realitat ls rep ls carta10.txt carta1.txt carta2.txt informe.txt.

Si cap fitxer coincideix amb el patró, Bash deixa el patró tal qual, sense expandir. Si no hi ha cap fitxer que comenci per g, cp g* /tmp intentarà copiar un fitxer que es diu literalment g*, i cp donarà error:

$ echo g*
g*

Aquest comportament es pot canviar amb dues opcions de shopt:

  • shopt -s nullglob: si no hi ha coincidències, el patró desapareix (útil en bucles for f in *.txt dins d’scripts, perquè no s’executin amb el patró literal).
  • shopt -s failglob: si no hi ha coincidències, Bash dona error i no executa la comanda (bash: no match: g*).

Comodins bàsics

ComodíCoincideix ambExemple
?Exactament un caràcter qualsevolls carta?.txt
*Zero o més caràcters qualssevolcp *.txt /copia/
[conjunt]Un caràcter del conjunt o rangmv fitxer[1-3].txt docs/
[!conjunt] o [^conjunt]Un caràcter que no sigui del conjuntls fitxer[!1-3].txt

Exemples amb carta1.txt, carta2.txt i carta10.txt:

$ ls carta?.txt
carta1.txt  carta2.txt
$ ls carta*.txt
carta10.txt  carta1.txt  carta2.txt

Fixa’t que carta?.txt no troba carta10.txt, perquè ? és un sol caràcter.

Amb fitxer1.txt … fitxer4.txt:

$ ls fitxer[1-3].txt
fitxer1.txt  fitxer2.txt  fitxer3.txt
$ ls fitxer[!1-3].txt
fitxer4.txt

Dins dels claudàtors també pots fer servir classes de caràcters, com [[:digit:]], [[:upper:]] o [[:alpha:]]. Per exemple, ls [[:upper:]]* llista els fitxers que comencen amb majúscula.

Dues coses que convé saber:

  • Els fitxers ocults no hi entren. * no coincideix amb els noms que comencen per punt (.bashrc, .ocult). Per incloure’ls, escriu el punt explícitament (.*) o activa shopt -s dotglob.
  • Per desactivar un comodí, posa’l entre cometes o escapa’l amb \: echo '*.txt', echo "*.txt" i echo \*.txt mostren *.txt literalment. És el que has de fer, per exemple, amb find . -name '*.txt', perquè sigui find qui interpreti el patró i no el shell.

L’expansió de la titlla (~)

Tècnicament no és un comodí, sinó una altra expansió del shell, però va molt bé per escriure rutes curtes:

EscriusEs converteix en
~El teu directori personal ($HOME), p. ex. /home/oscar
~/carta.txt/home/oscar/carta.txt
~joanEl directori personal de l’usuari joan, p. ex. /home/joan
~joan/notes.txt/home/joan/notes.txt
$ echo ~ ~/carta.txt ~root ~noexisteix
/home/oscar /home/oscar/carta.txt /root ~noexisteix

Compte amb la barra: ~/notes.txt és un fitxer del teu directori, però ~notes.txt vol dir «el directori de l’usuari notes.txt». Com que aquest usuari no existeix, Bash deixa el text sense tocar i obtindràs un error de fitxer no trobat.


L’expansió de claus ({})

A diferència dels comodins, les claus no miren quins fitxers hi ha al disc: generen una paraula per cada element de la llista, existeixi o no. Per això també serveixen per crear fitxers i directoris nous.

$ echo fitxer{1..5}.txt
fitxer1.txt fitxer2.txt fitxer3.txt fitxer4.txt fitxer5.txt
$ echo {a..e} {01..10..3}
a b c d e 01 04 07 10

Usos pràctics del dia a dia:

# Crear una estructura de directoris de cop
mkdir -p projecte/{src,doc,test}

# Fer una còpia de seguretat ràpida d'un fitxer (copia config.conf a config.conf.bak)
cp config.conf{,.bak}

Les claus també superen una limitació dels claudàtors, que només poden triar un caràcter: amb claus pots triar entre paraules senceres. *.{h,c,cpp,o} s’expandeix a *.h *.c *.cpp *.o i després cada patró es busca al disc. Però compte: si una de les extensions no té cap fitxer, aquell patró queda literal i la comanda en rebrà el text:

$ echo *.{h,cpp,zz}
clau.h clau.cpp *.zz

Comodins estesos de Bash (extglob)

Bash té un joc de comodins més potents que s’activen amb l’opció extglob:

shopt -s extglob

A Debian i Ubuntu, en un terminal interactiu normalment ja està activada (la hi activa el paquet bash-completion), però en un script està desactivada i l’has d’activar tu. Ho pots comprovar amb shopt extglob.

ComodíCoincideix amb
?(llista)Zero o una ocurrència dels patrons
*(llista)Zero o més ocurrències
+(llista)Una o més ocurrències
@(llista)Exactament un dels patrons
!(llista)Qualsevol cosa excepte els patrons

La llista és un o més patrons separats per |, i cada patró pot tenir altres comodins. Per exemple, +([0-9]) coincideix amb una tira d’un o més dígits.

Exemples

Totes les cartes numerades, tinguin un dígit o més (carta?.txt no trobava carta10.txt):

$ ls carta+([0-9]).txt
carta10.txt  carta1.txt  carta2.txt

Fitxers amb algunes extensions concretes:

$ ls *.@(c|o|h|cpp)
clau.cpp  clau.h

També s’hauria pogut escriure @(*.c|*.o|*.h|*.cpp), perquè els patrons es poden niuar. A diferència de *.{c,o,h,cpp}, aquí no queda cap patró literal si una extensió no té fitxers.

Tots els fitxers excepte els HTML, GIF i JPG:

$ ls !(*.html|*.gif|*.jpg)

En canvi, *.!(html|gif|jpg) no fa el mateix, i l’error és subtil: el * pot agafar part de l’extensió. Amb un fitxer anomenat arxiu.tar.html, el * agafa arxiu, el punt coincideix amb el primer punt i !(html|gif|jpg) coincideix amb tar.html (que no és ni html ni gif ni jpg), així que el fitxer s’inclou. A més, aquest patró deixa fora els fitxers sense extensió.

Esborrar tots els fitxers del directori excepte els que es diuen vt seguit d’un o més dígits (vt1, vt22…):

$ echo !(vt+([0-9]))
...
$ rm !(vt+([0-9]))

Amb rm i patrons de negació, prova sempre abans amb echo i comprova la llista: una errada al patró pot esborrar just el que volies conservar.


Altres opcions útils de shopt

OpcióQuè fa
nullglobUn patró sense coincidències desapareix
failglobUn patró sense coincidències dona error
dotglob* també inclou els fitxers ocults
nocaseglobEls patrons no distingeixen majúscules de minúscules
globstar** recorre tots els subdirectoris: ls **/*.txt

Per exemple, amb globstar pots trobar tots els .txt d’un arbre de directoris sense fer servir find:

$ shopt -s globstar
$ echo **/*.txt
a/b/c/z.txt a/b/y.txt a/x.txt carta10.txt carta1.txt carta2.txt informe.txt

shopt -s opció l’activa i shopt -u opció la desactiva. Si vols que algun canvi sigui permanent, posa’l al teu ~/.bashrc (en tens més detalls a la guia de /bin/bash).


Conclusió

Els comodins són una de les eines que més temps estalvien al terminal. Recorda les tres idees clau: els expandeix el shell abans d’executar la comanda, un patró sense coincidències es queda tal qual (tret que activis nullglob o failglob), i prova sempre els patrons amb echo abans de fer-los servir amb comandes que esborren o mouen fitxers.

Posts relacionats

Esta web usa cookies.