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 buclesfor f in *.txtdins 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 amb | Exemple |
|---|---|---|
? | Exactament un caràcter qualsevol | ls carta?.txt |
* | Zero o més caràcters qualssevol | cp *.txt /copia/ |
[conjunt] | Un caràcter del conjunt o rang | mv fitxer[1-3].txt docs/ |
[!conjunt] o [^conjunt] | Un caràcter que no sigui del conjunt | ls 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 activashopt -s dotglob. - Per desactivar un comodí, posa’l entre cometes o escapa’l amb
\:echo '*.txt',echo "*.txt"iecho \*.txtmostren*.txtliteralment. És el que has de fer, per exemple, ambfind . -name '*.txt', perquè siguifindqui 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:
| Escrius | Es converteix en |
|---|---|
~ | El teu directori personal ($HOME), p. ex. /home/oscar |
~/carta.txt | /home/oscar/carta.txt |
~joan | El 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 |
|---|---|
nullglob | Un patró sense coincidències desapareix |
failglob | Un patró sense coincidències dona error |
dotglob | * també inclou els fitxers ocults |
nocaseglob | Els 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
- Les comandes internes de Bash
ls: llistar fitxers i directoris.find: cercar fitxers per nom, mida, data…- Els fitxers d’inici de sessió de Bash