La lliçó anterior va acabar amb una frontera clara: awk llegeix i resumeix, però no és l'eina per reescriure text conservant-ne la forma. Quan cal anonimitzar les IP d'acces.log abans d'enviar-lo a un proveïdor, arreglar els separadors d'un CSV mal exportat o canviar una clau de veloz-ops.conf sense obrir un editor, el que fa falta és un editor de flux: un programa que rebi text per un costat, apliqui una llista d'ordres d'edició a cada línia i deixi anar el resultat per l'altre. Això és sed, i funciona amb les mateixes expressions regulars que ja domines des de 05-04, només que aplicades a transformar en lloc de a filtrar.
Contingut
- El cicle de sed i l'espai de patrons
sed,awko${var//a/b}: quin fer servir- Sintaxi i opcions
-i: editar fitxers al lloc, amb xarxa de seguretat- Adreces: a quines línies s'aplica una ordre
- L'ordre
s///a fons - Altres ordres:
p,d,q,a,i,c,y - Filtrar i extreure trams
- Encadenar ordres i blocs
- Espai de retenció i
N, a grans trets - Receptes reals del toolkit
- Aplicació:
veloz_anonimitza_logalib/comu.sh
- El cicle de sed i l'espai de patrons
sed no veu el fitxer sencer: treballa línia a línia sobre una memòria intermèdia anomenada espai de patrons. Per a cada línia de l'entrada repeteix sempre el mateix cicle.
flowchart LR
A[Llegeix una linia] --> B["La copia a<br/>l'espai de patrons"]
B --> C[Aplica les ordres<br/>en ordre]
C --> D{-n actiu?}
D -- No --> E["Imprimeix<br/>l'espai de patrons"]
D -- Si --> F[No imprimeix res]
E --> G["Esborra l'espai<br/>i seguent linia"]
F --> G
G --> A
D'aquest esquema en surten tres conseqüències que expliquen gairebé tot el comportament de sed:
- Les ordres s'apliquen en ordre sobre la mateixa memòria intermèdia. Si la primera substitueix
AperB, la segona ja veuB. Encadenar substitucions no és el mateix que aplicar-les per separat. - La impressió és automàtica al final del cicle, no la fa l'ordre. Per això
sed 's/a/b/'imprimeix totes les línies, no només les que ha canviat. -napaga aquesta impressió automàtica, i a partir d'aquí només surt el que demanis explícitament ambp. D'aquí ve l'idiomased -n '...p'.
sed, awk o ${var//a/b}: quin fer servir
sed, awk o ${var//a/b}: quin fer servirLes tres eines substitueixen text, i triar malament és la causa més habitual de codi enrevessat:
${var//a/b} (04-04) |
sed |
awk (06-01) |
|
|---|---|---|---|
| Opera sobre | Una variable en memòria | Un flux o fitxer | Un flux o fitxer |
| Cost | Zero: és Bash pur | Un procés extern | Un procés extern |
| Emparellament | Globs, no regex | Regex (BRE o ERE) | Regex (ERE) |
| Entén de camps | No | No (només línies) | Sí: $1, $NF… |
| Sap fer comptes | Només enters | No | Sí, en coma flotant |
| Recorda entre línies | No hi aplica | Només amb l'espai de retenció | Sí: variables i arrays |
| Fort en | Un valor solt | Reescriure text | Agregar i calcular |
La regla pràctica: si el text ja és en una variable, ${var//a/b} i no llancis cap procés; si cal reescriure un fitxer o un flux, sed; si cal sumar, comptar o mirar columnes, awk. I si et trobes comptant camps amb sed, o substituint amb awk en diverses passades, gairebé segur que has triat l'eina equivocada.
- Sintaxi i opcions
Les ordres van entre cometes simples pel mateix motiu que a awk: contenen $, & i barres invertides que Bash expandiria. Les opcions que es fan servir de debò:
| Opció | Què fa |
|---|---|
-n |
Suprimeix la impressió automàtica (vegeu la secció 1) |
-e 'cmd' |
Afegeix un guió d'ordres; es pot repetir per acumular-ne diversos |
-f fitxer.sed |
Llegeix les ordres d'un fitxer, com el -f d'awk |
-i[SUF] |
Edita el fitxer al lloc en comptes d'escriure a la sortida estàndard |
-E |
Fa servir ERE en lloc de BRE (-r és el sinònim antic de GNU) |
-z |
Separa registres per byte nul, per casar-lo amb find -print0 (05-01) |
-E mereix una recomanació explícita. Per defecte sed parla BRE, el dialecte de 05-04 en què +, ?, (, ) i | són literals i s'han d'escapar: s/[0-9]\+/N/. Amb -E escrius s/[0-9]+/N/, que és el que tens al cap. Fes servir -E sempre llevat que copiïs una recepta aliena escrita en BRE.
-i: editar fitxers al lloc, amb xarxa de seguretat
-i: editar fitxers al lloc, amb xarxa de seguretat-i és l'opció més útil i la més perillosa de sed: reescriu el fitxer original sense preguntar i sense possibilitat de desfer. Un patró mal escrit pot buidar un fitxer de configuració en producció.
Dos hàbits que eviten el desastre:
- Prova sempre sense
-iprimer. Executa l'ordre tal qual, mira la sortida, i només llavors afegeix-i. És gratis i resol el 95 % dels ensurts. - Fes servir un sufix de còpia,
-i.bak, no-ia seques. Costa cinc pulsacions i deixa l'original recuperable.
Aquí hi ha a més una incompatibilitat clàssica: a GNU sed (Linux) el sufix va enganxat, -i.bak, i -i tot sol és vàlid; a BSD/macOS -i exigeix argument, així que sense còpia s'escriu -i ''. Conseqüència: sed -i.bak funciona als dos, sed -i només a Linux, i un script que faci servir sed -i '' fallarà a Linux interpretant '' com a nom de fitxer. Si l'script ha de córrer als dos llocs, la sortida neta és escriure a un temporal (mktemp, 05-01) i moure'l a sobre amb mv.
I un avís seriós: no llancis mai sed -i sobre fitxers d'/etc sense còpia prèvia. sed -i 's/PermitRootLogin no/PermitRootLogin yes/' /etc/ssh/sshd_config és un canvi de seguretat fet a cegues, sense validació i sense marxa enrere. Les implicacions es veuen a 08-03.
- Adreces: a quines línies s'aplica una ordre
Una ordre sense adreça s'aplica a totes les línies. Anteposant-li una adreça es restringeix:
| Adreça | S'aplica a |
|---|---|
5 |
Només la línia 5 |
$ |
L'última línia |
/ERROR/ |
Les línies que casen amb la regex |
2,5 |
De la línia 2 a la 5 |
2,$ |
De la 2 fins al final |
/inici/,/fi/ |
Del primer emparellament d'una al següent de l'altra |
/ERROR/,+3 |
La línia que casa i les 3 següents (GNU) |
0~3 |
Una de cada 3 línies (GNU: primera~pas) |
/ERROR/! |
Negació: totes les que NO casen |
Així, sed -n '/2026-08-03 10:/,/2026-08-03 11:/p' /var/log/veloz/app.log extreu una hora del log, i sed '1!d' enviaments.csv deixa només la capçalera. El ! es llegeix «excepte»: 1!d és «esborra les línies que no són la 1». És la manera idiomàtica de conservar només una cosa, i apareix constantment a les receptes reals.
- L'ordre
s/// a fons
s/// a fonsÉs l'ordre estrella i la que justifica el 90 % dels usos de sed. La seva forma és s/regex/reemplacament/banderes:
El delimitador no ha de ser /. Qualsevol caràcter val, i triar bé evita la «síndrome de les barres inclinades» quan treballes amb camins:
sed 's|/srv/veloz/dades|/mnt/copia/dades|' camins.txt # llegible
sed 's/\/srv\/veloz\/dades/\/mnt\/copia\/dades/' camins.txt # el mateix canvi, illegibleLes banderes que van després de l'últim delimitador:
| Bandera | Efecte |
|---|---|
g |
Substitueix totes les ocurrències de la línia, no només la primera |
i |
Ignora majúscules i minúscules |
p |
Imprimeix la línia si hi ha hagut substitució (es fa servir amb -n) |
w f |
Escriu al fitxer f les línies substituïdes |
2 |
Substitueix només la 2a ocurrència; 2g des de la 2a endavant |
Al reemplaçament hi ha dos metacaràcters que cal conèixer. & significa «tot el que ha casat», i \1, \2… són els grups de captura de 05-04, amb els grups escrits \\(...\\) en BRE o (...) amb -E:
# Posar entre claudators el nivell d'app.log: [ERROR] -> [[ERROR]]
sed -E 's/\[(INFO|WARN|ERROR)\]/[&]/' /var/log/veloz/app.log
# Reordenar "data nivell" a "nivell data" fent servir dos grups
sed -E 's/^([0-9-]+ [0-9:]+) \[([A-Z]+)\]/[\2] \1/' /var/log/veloz/app.logCorol·lari pràctic: si vols un & literal al reemplaçament, escapa'l (\&), igual que cal escapar el delimitador si apareix dins del patró. I recorda que s/// sense g canvia només la primera ocurrència de cada línia: és l'oblit número u.
- Altres ordres:
p, d, q, a, i, c, y
p, d, q, a, i, c, y| Ordre | Què fa | Exemple |
|---|---|---|
p |
Imprimeix l'espai de patrons (amb -n, és l'única que imprimeix) |
sed -n '5p' |
d |
Esborra la línia i comença el cicle següent | sed '/^#/d' |
q |
Surt de sed immediatament | sed '100q' |
a text |
Afegeix una línia després | sed '$a fi informe' |
i text |
Insereix una línia abans | sed '1i id,data,ciutat' |
c text |
Canvia la línia sencera | sed '/obsolet/c # linia retirada' |
y/abc/xyz/ |
Tradueix caràcter a caràcter, com tr (02-02) |
y/aeiou/AEIOU/ |
d té una peculiaritat important: avorta el cicle, així que les ordres posteriors no s'executen sobre aquesta línia. I q és un optimitzador silenciós: sed '100q' fitxer_gegant deixa de llegir a la línia 100, mentre que head -100 amb una canonada continuaria llegint. La sintaxi d'a/i/c amb text a la mateixa línia és una extensió de GNU còmoda però no portable; la forma POSIX fa servir una barra invertida i un salt de línia.
- Filtrar i extreure trams
Amb -n i p es filtra igual que amb grep: sed -n '/ERROR/p' app.log fa exactament el mateix que grep ERROR app.log. Si només vas a filtrar, fes servir grep. És més llegible, sol ser més ràpid, i té -c, -v, -i, -l i -o per als casos habituals. sed guanya tan bon punt l'operació deixa de ser «sí o no» i passa a ser «transformar», o quan el que vols és un tram per número de línia, que grep no sap fer:
sed -n '10,20p' /var/log/veloz/app.log # linies 10 a 20
sed -n '10,20p;20q' /var/log/veloz/app.log # igual, pero deixa de llegir a la 20Aquest ;20q és el detall que marca la diferència en un log de dos gigues: sense ell, sed continua llegint fins al final encara que ja no imprimeixi res.
- Encadenar ordres i blocs
Diverses ordres se separen amb ; o amb diversos -e. I si vols aplicar un grup d'ordres a la mateixa adreça, s'agrupen amb claus:
sed -E '/^#/d; /^[[:space:]]*$/d' ~/veloz-ops/etc/veloz-ops.conf # treu comentaris i buides
sed -n '/inici informe/,/fi informe/{ s/^/ /; p }' informe.txt # sagna nomes aquest blocAl segon exemple, { s/^/ /; p } s'aplica únicament a les línies del rang: primer sagna, després imprimeix. L'ordre importa —recorda la secció 1: les ordres veuen la memòria intermèdia tal com l'ha deixat l'anterior—. Si invertissis p i s, imprimiries la línia sense sagnar.
- Espai de retenció i
N, a grans trets
N, a grans tretsA més de l'espai de patrons, sed té una segona memòria intermèdia, l'espai de retenció, que sobreviu entre línies. Es maneja amb h (copiar patró→retenció), H (afegir), x (intercanviar) i G (afegir retenció→patró). És el que dona a sed una memòria mínima: sed G informe.txt afegeix una línia en blanc després de cada línia, i el críptic sed '1!G;h;$!d' fitxer inverteix l'ordre de les línies —el tac de sed—.
L'ordre N afegeix la línia següent a l'espai de patrons, i permet que una regex creui dues línies —útil per ajuntar una línia d'app.log amb la seva traça—. Dit això, aquí hi ha el límit honest de l'eina: tan bon punt necessitis aquest nivell d'acrobàcia, awk o un llenguatge de guió seran més llegibles i més fàcils de mantenir. Sàpigues que existeix, no ho converteixis en el teu estil.
- Receptes reals del toolkit
Anonimitzar les IP d'acces.log conservant el primer octet per saber la xarxa d'origen, i normalitzar els separadors d'un CSV exportat amb punt i coma i coma decimal:
sed -E 's/^([0-9]{1,3})\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/\1.x.x.x/' /var/log/veloz/acces.log
sed -E 's/;/,/g; s/([0-9]),([0-9]{2})$/\1.\2/' enviaments-cru.csv > enviaments.csvCanviar una clau de configuració de forma idempotent, és a dir, que es pugui executar mil vegades amb el mateix resultat i que funcioni tant si la clau existia com si no:
if grep -q '^LLINDAR_DISC=' "$CONF"; then
sed -i.bak -E 's/^LLINDAR_DISC=.*/LLINDAR_DISC=85/' "$CONF"
else
printf 'LLINDAR_DISC=85\n' >> "$CONF"
fiL'if no és cap adorn: sed no pot afegir una línia que no existeix, només transformar les que hi ha. Sense la comprovació prèvia, l'script semblaria funcionar i no canviaria res. Fixa't en l'àncora ^, que evita tocar una clau que només contingui aquest text, i en el .* que es menja el valor antic sigui quin sigui.
Netejar un fitxer de configuració per veure'l sense soroll, i inserir una capçalera a un CSV que la va perdre:
sed -E '/^[[:space:]]*#/d; /^[[:space:]]*$/d' /etc/veloz/api.conf
sed '1i id_enviament,data,ciutat,repartidor,estat,import' sense-capcalera.csv > enviaments.csv
- Aplicació:
veloz_anonimitza_log a lib/comu.sh
veloz_anonimitza_log a lib/comu.shLa recepta de la IP es converteix en funció reutilitzable, seguint les regles de llibreria de 05-06: només definició, prefix d'espai de noms, sense codi de nivell superior:
# veloz_anonimitza_log — Anonimitza IP i correus d'un log. Us: veloz_anonimitza_log <fitxer>
# Escriu el resultat per la sortida estandard; no toca l'original.
veloz_anonimitza_log() {
local origen="${1:?falta el fitxer de log}"
[[ -r "$origen" ]] || { veloz_log_error "no puc llegir $origen"; return 66; }
sed -E -e 's/^([0-9]{1,3})\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/\1.x.x.x/' \
-e 's/[[:alnum:]._%+-]+@[[:alnum:].-]+\.[[:alpha:]]{2,}/<correu>/g' \
-- "$origen"
}Tres decisions mereixen comentari. La funció no fa servir -i: escriu a la sortida estàndard i deixa que qui la crida decideixi (veloz_anonimitza_log "$LOG" > /tmp/compartir.log), que és el correcte en una llibreria —una funció que modifica fitxers aliens per sorpresa és un parany—. Els dos -e separats són més llegibles que un ; llarg i permeten comentar-ne un en depurar. I el -- marca el final d'opcions, així que un fitxer anomenat -estrany.log es tracta com a fitxer i no com a opció; és el mateix hàbit defensiu de 03-05. El codi 66 és EX_NOINPUT, de la taula de 05-03.
Verificar abans de confiar és obligatori amb sed: una comprovació ràpida com head -3 /var/log/veloz/acces.log | veloz_anonimitza_log /dev/stdin n'hi ha prou per veure que les IP surten emmascarades i la resta de la línia intacta.
Errors Habituals i Consells
- Oblidar la bandera
g.s/,/;/canvia només la primera coma de cada línia. Per a totes,s/,/;/g. - Fer servir
-isense provar abans. Executa sempre l'ordre sense-i, mira la sortida i només llavors edita. I fes servir-i.bak, que funciona a GNU i a BSD. - Esperar ERE per defecte.
sed 's/[0-9]+/N/'no fa res, perquè en BRE+és un+literal. Afegeix-E. - Escapar barres a mà. Si el patró porta camins, canvia de delimitador:
s|/a/b|/c/d|. - Confondre el
.dins d'una IP.[0-9]{1,3}.[0-9]casa també amb192x168; el punt literal s'escriu\.. - Creure que
sedpot afegir claus que falten. Només transforma línies existents; per afegir, comprova ambgrep -qi fes servir>>. - Fer servir
sedper filtrar. Si és un «sí o no»,grep; si cal canviar el text,sed; si hi ha columnes o comptes,awk. - Consell: amb fitxers grans,
sed -n '10,20p;20q'ised '100q'eviten llegir de més.LC_ALL=C sed ...també accelera. - Consell: un guió de sed llarg es treu a un fitxer
.sedi s'invoca amb-f, igual que a awk: es comenta amb#i es versiona.
Exercicis
Exercici 1. Escriu una ordre que extregui de /var/log/veloz/app.log únicament les línies de l'interval entre 10:00:00 i 11:00:00 i els tregui la data (deixant només hora, nivell i missatge), sense modificar el fitxer original.
Exercici 2. Escriu una funció veloz_set_conf per a lib/comu.sh que fixi una clau a un valor a ~/veloz-ops/etc/veloz-ops.conf de forma idempotent, amb còpia de seguretat, i que conservi els permisos 600 del fitxer.
Exercici 3. El proveïdor envia enviaments-cru.csv amb separador ;, imports amb coma decimal, espais sobrants al voltant dels camps i una línia de comentari inicial que comença per #. Escriu un sol sed que el deixi en el format d'enviaments.csv.
Solucions
Solució 1.
El rang /inici/,/fi/ selecciona el tram, i les claus apliquen les dues ordres només a ell: primer es treu la data amb s/// ancorat a ^, després s'imprimeix amb p. -n és imprescindible —sense ell sortiria el fitxer sencer, amb el tram a més duplicat pel p—. I l'ordre dins de les claus importa: si p anés primer, imprimiria la línia amb la data encara posada.
Solució 2.
# veloz_set_conf — Fixa clau=valor de forma idempotent. Us: veloz_set_conf CLAU valor
veloz_set_conf() {
local clau="${1:?falta la clau}" valor="${2-}" conf="${VELOZ_CONF:?}"
[[ "$clau" =~ ^[A-Z_][A-Z0-9_]*$ ]] || { veloz_log_error "clau invalida: $clau"; return 64; }
if grep -q "^${clau}=" "$conf"; then
sed -i.bak -E "s|^${clau}=.*|${clau}=${valor}|" "$conf"
else
printf '%s=%s\n' "$clau" "$valor" >> "$conf"
fi
chmod 600 "$conf"
}Aquí sí que es fan servir cometes dobles, perquè cal interpolar $clau i $valor, i per això la validació prèvia amb =~ (05-04) no és opcional: sense ella, una clau amb | o & trencaria la substitució o hi injectaria comportament. El delimitador | evita problemes si el valor és un camí, i chmod 600 al final restaura els permisos, perquè sed -i crea un fitxer nou i el reanomena, de manera que els permisos poden canviar. El codi 64 és EX_USAGE.
Solució 3.
sed -E '/^#/d; s/[[:space:]]*;[[:space:]]*/,/g; s/^[[:space:]]+//; s/[[:space:]]+$//; s/([0-9]),([0-9]{2})$/\1.\2/' \
enviaments-cru.csv > /srv/veloz/dades/enviaments.csvL'ordre és deliberat i és tot l'exercici. Primer s'esborra el comentari (d avorta el cicle, així que aquesta línia ni es processa). Després es normalitzen els ; absorbint els espais que els envolten, cosa que resol de cop el sobrant interior. Tot seguit es retallen els extrems de la línia. I només al final s'arregla el decimal: si aquesta substitució anés abans, la , acabada de crear pel pas del ; podria confondre's amb el decimal. Convé verificar-ho amb head -3 enviaments-cru.csv | sed -E '...' abans de sobreescriure res.
Conclusió
sed és un editor de flux: per cada línia la copia a l'espai de patrons, li aplica les ordres en ordre i la imprimeix en acabar el cicle llevat que -n ho impedeixi —d'aquí l'idioma sed -n '...p'—. Les seves ordres accepten adreces (número de línia, $, regex, rangs 2,5 i /inici/,/fi/, i la negació !) que decideixen a quines línies s'apliquen, i el rei de totes és s/regex/reemplacament/banderes, amb delimitador lliure per no barallar-se amb els camins, banderes g, i, p i w, i un reemplaçament on & és allò que ha casat i \1 els grups de 05-04. Al voltant hi ha d, p, q, a, i, c i y, els blocs { } per agrupar per adreça, i l'espai de retenció (h, x, G, N) com a memòria mínima que convé conèixer i no abusar-ne. -E per escriure ERE còmodament, -i.bak sempre amb còpia i provant abans sense -i, i la frontera clara amb els seus veïns: ${var//a/b} per a una variable, grep per filtrar, awk per a columnes i comptes, sed per reescriure.
Amb awk i sed, el toolkit ja sap llegir, resumir i transformar qualsevol text que li tiris. Però tot aquest text continua venint de fitxers que algú va deixar al disc. Un script d'operacions necessita alguna cosa més: preguntar al mateix sistema en quin estat és —quant disc queda, quina càrrega suporta, qui hi ha connectat, quina versió de sistema operatiu corre, si el servei veloz-api és viu— per poder decidir en funció de la resposta. Això és la lliçó següent (06-03): les ordres que interroguen el sistema i com convertir les seves respostes en llindars i decisions dins d'un script.
Curs de Programació en Bash
Mòdul 1: Introducció a Bash
- Què és Bash?
- Configurar el teu Entorn
- Navegació Bàsica per la Línia d'Ordres
- Entendre el Shell
- Trobar Ajuda: man, help i --help
Mòdul 2: Ordres Bàsiques de Bash
- Operacions amb Fitxers i Directoris
- Ordres de Processament de Text
- Permisos i Propietat dels Fitxers
- Redirecció i Canonades
- Comodins i Expansió de Rutes
- Historial i Dreceres de Teclat
Mòdul 3: Fonaments de Scripting
- Crear i Executar un Script
- Variables i Constants
- Operadors Bàsics
- Sentències Condicionals
- Arguments i Entrada de l'Usuari
- Cometes, Expansió i Substitució
Mòdul 4: Scripting Intermedi
- Bucles en Bash
- Funcions en Bash
- Arrays i Arrays Associatius
- Manipulació de Cadenes
- La Sentència case i els Menús Interactius
- Aritmètica i Càlculs Numèrics
Mòdul 5: Tècniques Avançades de Scripting
- Operacions Avançades amb Fitxers
- Gestió de Processos
- Gestió d'Errors i Depuració
- Expressions Regulars
- Entrada/Sortida Avançada: Descriptors i Here-Documents
- Scripts Modulars i Llibreries Reutilitzables
Mòdul 6: Treballar amb Eines Externes
Mòdul 7: Automatització i Programació
- Tasques Cron
- Automatitzar Tasques
- Scripts de Còpia i Restauració
- Monitoratge i Registre
- Serveis i Temporitzadors amb systemd
- Automatització Remota amb SSH
Mòdul 8: Bones Pràctiques i Optimització
- Escriure Codi Llegible
- Optimitzar Scripts en Bash
- Consideracions de Seguretat
- Control de Versions amb Git
- Anàlisi Estàtica amb ShellCheck i shfmt
- Proves Automatitzades amb Bats
- Portabilitat: POSIX sh enfront de Bashismes
