Sed Insertar y Añadir Texto: Ejemplos y Gotchas de macOS
Domina los comandos sed de insertar y añadir texto: números de línea, patrones, bloques multilínea, edición in-place en GNU y BSD sed, y los gotchas de macOS a evitar.
43 min de lectura

Los comandos sed insert (i) y sed append (a) añaden texto antes o después de cualquier línea de un archivo, apuntados por número de línea o coincidencia de patrón. Una trampa: la sintaxis de una línea sed '4i text' que la mayoría de tutoriales enseñan falla en el sed de macOS/BSD. Esta guía cubre primero la forma portable que funciona en todas partes, luego números de línea, patrones, bloques multilínea, edición in-place segura, y los modos de fallo silenciosos (archivos vacíos, streams multi-archivo, hard links) que aparecen en scripts.
GNU sed vs BSD sed (macOS)
La mayoría de distribuciones Linux usan GNU sed, macOS trae BSD sed, y las imágenes Alpine/Docker traen BusyBox sed. Dos diferencias importan para este artículo:
- Edición in-place: GNU sed usa
sed -i '...' file. BSD sed (macOS) necesita un argumento de sufijo de backup:sed -i '' '...' file. - Insert/append de una línea:
sed '4i text'funciona en GNU sed y BusyBox sed, pero BSD/macOS sed requiere la formai\+ newline.
Si quieres un enfoque que funcione en todas partes, usa la sintaxis i\ + newline y escribe a un archivo temporal en vez de -i (ejemplos abajo).
Probado con GNU sed 4.9 y BusyBox sed 1.36.1 en Linux. El GNU sed actual es 4.10 (publicado 2026-04-21); Ubuntu 24.04 y distros era-Debian-12 siguen trayendo 4.9. Nada en 4.10 cambió el manejo de i, a o c, así que cada ejemplo de abajo aplica a ambas.
¿Qué es sed?
sed (Stream Editor) es una utilidad de línea de comandos para filtrar y transformar texto en sistemas tipo Unix. Procesa archivos línea a línea sin interacción del usuario, lo que lo hace una buena pieza para scripts y operaciones por lotes. Junto a herramientas como grep, awk y find, pertenece a tu conjunto de comandos esenciales de Linux.
Ventajas para insertar y añadir texto:
- No interactivo: corre hasta el final una vez invocado, sin prompts
- Posicionamiento preciso: inserta o añade en números de línea específicos o coincidencias de patrón
- Ligero en memoria: procesa una línea cada vez (una salvedad: el arreglo estrella de sed 4.10 fue
s///ghaciendo pánico en líneas de más de 2GB, así que las líneas sueltas gigantes eran un modo de fallo real hasta hace poco) - Scripteable: funciona en pipes, bucles y trabajos de CI junto al resto del toolkit Unix
Otras guías de sed para manipulación de texto:
- Borrar líneas con sed usando el comando
d - Insertar o añadir texto con los comandos
iya(esta guía) - Cambiar mayúsculas con sed para estandarización
- Sed buscar y reemplazar con coincidencia de patrones
Si quieres un GNU sed de verdad para practicar en vez de lo que tenga tu portátil, un Hetzner Cloud VPS empieza alrededor de 4-5 EUR/mes y te da el entorno Linux exacto que estos ejemplos asumen. Un Hostinger VPS económico también sirve.
¿Qué sed estás corriendo?
Antes de copiar cualquier ejemplo, comprueba en qué implementación estás. La sintaxis de una línea se comporta distinto entre ellas.
GNU sed (Linux)
sed --version
# sed (GNU sed) 4.9
# or: sed (GNU sed) 4.10i text/a textde una línea: funciona (extensión GNU)sed -i '...' file: funciona, sin sufijo de backup necesariosed -s: soportado (modo por-archivo para múltiples entradas)- Esto es lo que asumen los ejemplos de distro de esta guía
BSD sed (macOS)
which sed
# /usr/bin/sed
man sed
# no --version flag on BSD sedi text/a textde una línea: falla. Usa la formai\+ newline- In-place necesita un sufijo explícito:
sed -i '' '...' file - Sin flag
-s - Esta es la implementación sobre la que van los “gotchas de macOS” de esta guía
BusyBox sed (Alpine/Docker)
sed --help 2>&1 | head -1
# BusyBox v1.36.1 sedi text/a textde una línea: funciona, como GNUsed -i '...' file: funciona- Archivo vacío +
$a/1i: no-op silencioso, igual que GNU - Las imágenes basadas en Alpine traen BusyBox sed, lo que vale saber cuando escribes sed en un Dockerfile y lo pruebas en tu Mac primero. Para el resto del toolkit de contenedores, mira los comandos Docker imprescindibles.
Resumen rápido de capacidades:
| Sintaxis / flag | GNU sed | BusyBox sed | BSD sed (macOS) |
|---|---|---|---|
i text de una línea |
funciona | funciona | falla |
i\ + newline (forma POSIX) |
funciona | funciona | funciona |
-i sin sufijo de backup |
funciona | funciona | falla, usa -i '' |
$ = última línea del último archivo |
sí (a menos que -s) |
sí | sí |
-s (trata cada archivo como un stream separado) es una extensión GNU y no existe en BSD sed. En BusyBox, comprueba sed --help antes de confiar en él.
Una nota de portabilidad para scripts nuevos: -E para regex extendida está estandarizado desde POSIX.1-2024 (Issue 8). Prefiere -E sobre el viejo flag GNU -r.
Insertando texto con sed
Solo imprimir vs edición in-place
Por defecto sed imprime la salida modificada a stdout y deja el archivo intacto. Empieza siempre aquí:
sed '4i Inserted line' example.txtSi la salida se ve bien, hazla permanente usando la sección “Edición in-place segura” más abajo. Las diferencias GNU vs BSD importan una vez que añades -i, así que no te saltes esa parte.
El comando i inserta texto antes de una línea especificada. Es la herramienta correcta para añadir headers, comentarios o entradas de configuración en posiciones concretas.
Sintaxis básica de insert
Hay dos formas. La portable viene de POSIX y funciona en sed de GNU, BusyBox y BSD:
sed 'LINE_NUMBER i\
TEXT' filenameLa de una línea es más corta y es lo que la mayoría de blogs enseñan:
sed 'LINE_NUMBER i TEXT' filenameLa sintaxis i/a de una línea es una extensión GNU
La forma de una línea i text es aceptada por GNU sed y BusyBox sed, pero no está en el estándar POSIX. POSIX.1-2024 sigue requiriendo la forma con backslash a\ / i\. BSD/macOS sed rechaza la de una línea. Historia completa en la siguiente sección.
Por qué tu comando sed ‘1i text’ falla en macOS
Esta es la queja de sed más común de los usuarios de Mac. Copias un comando de tutorial como este:
Falla en macOS
sed '4i Inserted line' example.txtEn BSD sed esto da error. La redacción esperada viene del código de sed de FreeBSD, del que deriva el sed de macOS (confirma el texto exacto en un Mac real):
sed: command i expects \ followed by textNada se inserta. Lo mismo aplica a a y c.
Funciona en todas partes
sed '4i\
Inserted line' example.txtEl newline inmediatamente tras i\ es la clave. GNU sed, BusyBox sed y BSD sed aceptan todos esta forma, así que un script escrito así corre en un Mac, en una máquina Linux y dentro de un contenedor Alpine.
Regla de dedo
Si el script debe correr en macOS, usa siempre i\ + newline. Si solo corres en GNU/Linux (incluyendo Alpine/BusyBox), la de una línea vale y es menos ruidosa.
La misma regla cubre a (append) y c (change/replace line). Los tres comandos viven en el mismo bloque POSIX y comparten la rareza de sintaxis.
Insertar por número de línea
Insertar antes de una línea específica (primero la de una línea GNU/BusyBox, la forma portable debajo):
sed '4i This is the inserted line.' example.txt
# portable form: works on macOS too
sed '4i\
This is the inserted line.' example.txtInsertar al principio de un archivo:
sed '1i\
Header line goes here' filenameInsertar múltiples líneas (estilo portable):
sed '4i\
First inserted line\
Second inserted line\
Third inserted line' filenameNotas:
- El newline justo tras
i\es requerido para portabilidad entre GNU sed y BSD sed - Mantén la comilla de cierre al final de la última línea insertada
- Los números de línea originales bajan tras la inserción. Si insertas antes de la línea 4, la vieja línea 4 se vuelve la línea 5
Insertar por coincidencia de patrón
Insertar antes de líneas que coinciden con un patrón:
sed '/PATTERN/i\
TEXT' filenameEjemplos prácticos:
sed '/function main/i\
# Main function starts here' script.py
sed '/^server {/i\
# Nginx server configuration' nginx.conf
sed '/export PATH/i\
# Adding to PATH variable' .bashrcSi estás editando nginx.conf como parte de un setup de servidor real, mira cómo configurar Nginx en Linux para la configuración alrededor.
Ejemplos avanzados de insert
Insertar con variables (comilla con seguridad):
header="Generated on $(date)"
sed "1i\\
$header" datafile.txtPor qué este estilo: si tu variable contiene caracteres que sed trata de forma especial (guiones iniciales, backslashes, etc.), la forma i\ + newline se comporta más consistentemente entre plataformas que la de una línea.
Insertar bloques de configuración:
sed '/^# Database settings/i \
# Redis configuration\
redis.host=localhost\
redis.port=6379' config.iniInsertar tras encontrar contenido específico:
sed '/TODO:/i\
# FIXME: Review this section' code.pyPuntos a recordar
- El texto se inserta antes de la línea especificada o antes de cada línea que coincida con un patrón
- Los números de línea originales bajan tras una inserción
- Usa
\al final de cada línea para inserciones multilínea - sed escribe a stdout por defecto. Añade
-isolo después de previsualizar la salida
Añadiendo texto con sed
El comando a añade texto después de una línea especificada. Úsalo para footers, tags de cierre, o datos que deban seguir a contenido específico.
Sintaxis básica de append
Forma portable (POSIX, funciona en todas partes):
sed 'LINE_NUMBER a\
TEXT' filenameDe una línea (solo GNU + BusyBox):
sed 'LINE_NUMBER a TEXT' filenameAñadir por número de línea
Añadir después de una línea específica:
sed "2a\\
Don't forget to subscribe!" filenameAñadir al final del archivo. Este es el caso sed append to end of file que la gente busca, y $ es la dirección para ello:
sed '$a\
Footer text goes here' filename$ coincide con la última línea. Con múltiples archivos sin -i, “última línea” significa la última línea del último archivo. Mira la sección de gotchas.
Añadir múltiples líneas (estilo portable):
sed '4a\
First appended line\
Second appended line\
Third appended line' filenameA diferencia de insert, las líneas añadidas no desplazan los números de línea del contenido existente.
Añadir por coincidencia de patrón
Añadir después de líneas que coinciden con un patrón:
sed '/PATTERN/a\
TEXT' filenameEjemplos prácticos:
sed '/^}$/a\
# End of function block' script.js
sed '/^server {/a\
# Server configuration continues' nginx.conf
sed '/export PATH/a\
# PATH modified above' .bashrcEjemplos avanzados de append
Añadir secciones de configuración:
sed '/^# Database settings/a \
host=localhost\
port=5432\
database=myapp' config.iniAñadir con variables:
timestamp="Last modified: $(date)"
sed "\$a\
$timestamp" datafile.txtAñadir tras marcadores específicos:
sed '/<!-- INSERT HERE -->/a\
<div>New content</div>' template.htmlSi te encuentras pegando blobs de JSON o YAML a través del quoting de sed, para y lee la sección “Cuándo NO usar sed” más abajo. Hay herramientas mejores para ese trabajo.
Insert vs append vs change
i y a tienen un tercer hermano: c (change) reemplaza la línea coincidente por completo. Los tres aparecen en el mismo bloque POSIX.
| Comando | Posición | Ejemplo (una línea GNU/BusyBox) | Notas |
|---|---|---|---|
i |
Antes de línea/patrón | sed '5i text' |
Inserta arriba; los números de línea posteriores bajan |
a |
Después de línea/patrón | sed '5a text' |
Inserta abajo; los números de línea existentes se quedan |
c |
Reemplaza la línea | sed '5c text' |
La línea coincidente se descarta y se reemplaza |
En macOS/BSD sed, escribe la forma portable en su lugar: i\, a\ o c\ seguido de un newline y luego el texto.
Puntos a recordar
- El texto se añade después de la línea o patrón especificado
- Usa
$apara añadir al final de un archivo - Usa
\al final de línea para appends multilínea - Los números de línea existentes no cambian; las líneas nuevas aterrizan debajo
Apuntando posiciones específicas
La fuerza de sed es apuntar dónde debe aterrizar el texto, usando números de línea, rangos o patrones.
Apunte basado en posición
Números de línea:
sed '1i\
Header text' filename # Insert at beginning
sed '5a\
Middle text' filename # Append after line 5
sed '$a\
Footer text' filename # Append at endRangos de líneas. Ojo a la semántica: un comando con un rango corre una vez por línea en el rango, no una vez para el rango entero:
sed '10,15i\
# Section start' filename # Inserts before EACH line from 10 to 15
sed '20,$a\
# End section' filename # Appends after every line from 20 to EOFSi querías una inserción antes de la línea 10 solamente, direcciona la línea 10 (10i\), no el rango 10,15i. Esto tropieza a la gente regularmente.
Apunte basado en patrón
Patrones simples:
sed '/TODO/i\
# FIXME: Address this' filename
sed '/^function/a\
# Function definition ends' filename
sed '/^#/a\
# Comment continues' filenamePatrones complejos con expresiones regulares:
sed '/^[0-9]/i\
# Numbered item:' filename # Before lines starting with digits
sed '/\.log$/a\
# Log entry processed' filename # After lines ending with .log
sed '/^[[:space:]]*$/i\
# Empty line above' filename # Before blank linesTrabajo regex mixto (formas de identificadores, patrones de email):
sed '/^[A-Z][a-z]*[0-9]\{2,4\}/i\
# Code identifier found' data.txt
sed '/[a-zA-Z0-9._%+-]\+@[a-zA-Z0-9.-]\+\.[a-zA-Z]\{2,\}/a\
# Email processed' contacts.txtPara manipulación de texto que va más allá de insert/append, como reemplazar coincidencias o reestructurar líneas, la guía de sed buscar y reemplazar cubre el comando s en serio.
Casos de uso prácticos
Archivos de configuración:
# Add database configuration
sed '/^# Database/a \
host=localhost\
port=5432\
user=admin' config.ini
# Insert security headers
sed '/^server {/a \
add_header X-Frame-Options SAMEORIGIN;\
add_header X-Content-Type-Options nosniff;' nginx.confArchivos de código:
# Add function documentation
sed '/^def /i\
# Function: Performs calculation' script.py
# Insert debugging statements
sed '/^if /a\
print("Debug: Condition checked")' debug.pyProcesamiento de datos:
# Add CSV headers
sed '1i\
Name,Age,Email' data.csv
# Insert separators
sed '/^---/i\
# Section divider' document.txtTécnicas avanzadas de apunte
Inserción condicional (forma de bloque):
sed '/config_section/{
i\
# Configuration starts here
}' filenameDentro de bloques, quédate con la forma i\ + newline. Se comporta igual en GNU sed y BSD sed.
Operaciones múltiples (insert + append alrededor de la misma coincidencia):
sed '/important_line/{
i\
# Important section begins
a\
# Important section ends
}' filenameUsando variables para contenido dinámico:
section_name="Database Configuration"
sed "/^# $section_name/a\\
host=localhost" config.iniUn detalle solo-GNU: como extensión GNU, i y a aceptan dos direcciones (un rango en un solo comando). El sed POSIX solo acepta una. Mantén los scripts a una dirección si BSD/macOS tiene que correrlos.
Seguridad y pruebas
Previsualiza los cambios primero (siempre):
sed '/pattern/i\
NEW TEXT' filename
sed '/pattern/i\
NEW TEXT' filename | headMuestra números de línea para contexto:
nl -ba filename | sed '/pattern/i\
NEW TEXT'La verificación post-edición (diff, grep, conteos de líneas) vive en la sección “Verifica tu edición”. Hazla tras cualquier corrida in-place.
Edición in-place segura (GNU vs BSD sed)
GNU sed (la mayoría de Linux):
sed -i.bak '/pattern/i\
NEW TEXT' filenameBSD sed (macOS):
sed -i '.bak' '/pattern/i\
NEW TEXT' filenameAmbos crean filename.bak para que puedas revertir. El sufijo es la única diferencia: GNU toma -i.bak como una palabra, BSD quiere -i '.bak' (y -i '' si de verdad no quieres backup, lo cual yo no haría).
Edición in-place portable que preserva inodo, modo, propiedad y hard links:
tmp="$(mktemp)"
sed '/pattern/i\
NEW TEXT' filename > "$tmp" && cat "$tmp" > filename && rm -f "$tmp"Esto escribe el resultado a un archivo temporal, luego copia los bytes sobre el original con cat >. El objeto de archivo original nunca se reemplaza, así que los hard links y permisos sobreviven. La versión más simple mv "$tmp" filename también funciona, pero el archivo renombrado hereda el modo 0600 de mktemp y tu propiedad. Bien para archivos desechables, mal para cualquier cosa compartida. La historia del inodo está en la sección de gotchas.
Rollback
Con -i.bak o un sufijo .bak, mv filename.bak filename revierte la edición por completo. Sin un archivo de backup, deshacer significa re-correr la edición inversa o restaurar desde tu propio backup.
Gotchas: archivos vacíos, múltiples archivos y hard links
Estos son los modos de fallo que no aparecen en la mayoría de tutoriales de sed. Cada uno produce una situación de “pero el comando corrió bien”, que es exactamente lo que los hace caros en scripts.
Archivos vacíos: 1i y $a son no-ops silenciosos
Sobre un archivo de entrada vacío, 1i y $a no hacen nada. sed nunca arranca un ciclo, así que el texto de insert/append encolado nunca se imprime. Código de salida 0. Sin aviso. Verificado tanto en GNU sed como en BusyBox sed.
El manual de GNU describe el texto de i/a como encolado “al final del ciclo actual, o cuando se lee la siguiente línea de entrada”. Archivo vacío significa ninguna línea, ningún ciclo, ninguna salida.
¿Archivo vacío? sed no hará nada
Si corres sed '1i Header' empty.txt y el archivo sigue vacío, sed no está roto. Es comportamiento esperado. Usa un enfoque de shell en su lugar.
Alternativas seguras-con-archivo-vacío:
# Prepend a header (works on empty files too)
printf 'Header line\n' | cat - file.txt > out && cat out > file.txt && rm out
# Append a footer
echo 'Footer line' >> file.txtMúltiples archivos: un stream vs -s vs -i
Sin -i, pasar varios archivos hace que sed los trate como un stream continuo. Así que esto:
sed '$a END' f1 f2añade END solo después de la última línea de f2, no después de cada archivo. Si esperabas un footer en cada archivo, obtuviste un footer al mismísimo final y un resultado confuso.
Tus opciones:
sed -s '$a END' f1 f2 # GNU only: END after each file, on stdout
sed -i '$a END' f1 f2 # works per-file: -i implies -s in GNU sedEl manual de GNU lo dice claramente: la opción in-place implica -s. Por eso sed -i '1i Header' *.txt pone un header en cada archivo.
BSD sed no tiene -s, así que la respuesta portable por-archivo es un bucle:
for f in *.txt; do
sed '$a END' "$f" > "$f.tmp" && cat "$f.tmp" > "$f"
rm -f "$f.tmp"
doneInodos, hard links y permisos
sed -i no edita tu archivo. Escribe un archivo temporal y lo renombra sobre el original. La ruta se queda igual; el inodo no.
Consecuencias:
- Los hard links quedan rancios. Una ruta hermana hard-linkeada sigue apuntando al inodo viejo con el contenido viejo. Verificado: tras
sed -i, el hard link aún mostraba el texto original. - Los symlinks se reemplazan. Sin
--follow-symlinks, el propio symlink se sobrescribe con un archivo regular. - El modo se preserva. Un archivo
600se quedó600tras-ien pruebas. La propiedad también se preserva generalmente, pero verifica en tu sistema si corressed -ibajosudosobre archivos de otro usuario. Esa combinación es la que vale comprobar en vez de asumir.
Si necesitas que el inodo sobreviva (archivos compartidos, hard links, permisos raros), usa la receta cat > de la sección “Edición in-place segura” en vez de -i.
Para archivos de /etc symlinkeados, GNU sed tiene --follow-symlinks (solo válido con -i). sed 4.10 arregló una race de symlink TOCTOU en ese flag, buena razón para estar en una release actual.
Caso extremo del newline final
Si la última línea de un archivo no tiene newline final, sed '$a appended' sigue funcionando en GNU sed: añade el newline a la vieja última línea y añade normalmente. Se reporta que BSD sed se comporta distinto. Verifica antes de confiar en esto en macOS.
Dónde importa: archivos sensibles a bytes como assets generados o ciertas configs de /etc donde un newline final ausente es significativo. Comprueba con tail -c 1 file | xxd si no estás seguro.
Códigos de salida y ediciones de config idempotentes
sed sale con 0 coincida o no. Tu script no puede usar $? para preguntar “¿pasó el insert?”. Para aprovisionamiento de una vez está bien. Para cualquier cosa que corra dos veces (cron, gestión de config, scripts de setup re-ejecutables) necesitas un guard, o la línea aterriza dos veces:
grep -qxF 'export EDITOR=vim' ~/.bashrc || echo 'export EDITOR=vim' >> ~/.bashrc
grep -q '^PermitRootLogin' /etc/ssh/sshd_config || \
sed -i '/^#PermitRootLogin/a\
PermitRootLogin no' /etc/ssh/sshd_configEl segundo ejemplo es el patrón estándar de “descomenta un default de hardening exactamente una vez”. Si estás editando sshd_config en hosts remotos a través de un jumphost, el setup de SSH ProxyJump vale tenerlo primero para no bloquearte fuera probando.
Una nota de ops: a escala de flota, no hagas estos guards a mano. Los módulos lineinfile y blockinfile de Ansible existen exactamente para este trabajo: idempotencia, check mode y rollback ya están resueltos ahí. sed es la herramienta correcta para una máquina en la que estás ahora mismo; una herramienta de gestión de config es la correcta para mil máquinas.
Tabla rápida de modos de fallo
| Síntoma | Causa | Arreglo |
|---|---|---|
| El comando corrió, exit 0, archivo sin cambios | Archivo de entrada vacío, ningún ciclo de sed corrió | printf 'Header\n' | cat - file o echo >> file |
| El footer solo apareció en el último archivo | Múltiples archivos = un stream sin -s |
Usa sed -i (implica -s) o bucle por archivo |
El hard link sigue mostrando contenido viejo tras -i |
Archivo temporal + rename, inodo nuevo | Usa la receta in-place cat > |
| La línea aparece dos veces tras re-correr un script | sed sale 0 coincida o no | Guard grep -q antes de editar |
sed: command i expects \ followed by text |
BSD/macOS sed rechazó la de una línea | Usa la forma i\ + newline |
El comando r no produjo nada |
Archivo ausente o ilegible, silenciosamente vacío | Comprueba la ruta, luego grep -n después |
Verifica tu edición
Editar archivos de config a ciegas es cómo se consigue un servicio que no arranca en el próximo reinicio. Tres one-liners cazan casi todo.
Comprobaciones post-edición
sed -i.bak '/pattern/i\
NEW TEXT' file && diff file.bak file # review exactly what changed
grep -n 'NEW TEXT' file # confirm it landed (and landed once)
wc -l file.bak file # line-count sanitySi el diff está mal, mv file.bak file te devuelve atrás. Si necesitas comparar árboles enteros tras una edición masiva, mira comparar archivos y carpetas desde la terminal para la versión recursiva de esto.
- Diff revisado: los únicos cambios son las líneas que pretendías
grep -nencuentra exactamente una ocurrencia por objetivo (ni cero, ni dos)- El conteo de líneas se movió la cantidad esperada
- Servicio recargado o config validada si el archivo alimenta uno (
nginx -t,sshd -t,systemctl reload ...) - Solo entonces borra el
.bak
Traza la ejecución con sed –debug
GNU sed 4.6 y posteriores tiene --debug, que imprime el programa compilado y traza cada ciclo. Es la forma más rápida de ver por qué una dirección no coincidió:
$ printf 'x\n' | sed --debug '1a hello'
SED PROGRAM:
1 a\hello
INPUT: 'STDIN' line 1
PATTERN: x
COMMAND: 1 a\hello
END-OF-CYCLE:
x
helloObtienes el programa tal como sed lo parseó (útil cuando sospechas un error de quoting), la línea en la que está, y el pattern space en cada paso. sed 4.10 arregló un crash en el propio --debug, así que mantente al día si te apoyas en él.
--debug es solo-GNU. No existe en BSD/macOS sed ni en BusyBox sed.
Técnicas avanzadas de insert y append
Una vez sólidas las bases, estos patrones manejan los trabajos más liosos.
Adición condicional de texto
Insertar antes de líneas que coinciden con una condición:
# Insert a warning before error lines
sed '/ERROR/i\
*** WARNING: Critical error detected ***' logfile.txt
# Append configuration after specific sections
sed '/^# Network settings/a \
interface=eth0\
dhcp=true' config.txtCoincidencia de múltiples condiciones:
# Insert before lines starting with date patterns
sed '/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}/i\
Date entry:' dates.txt
# Append after lines containing both keywords
sed '/database.*config/a\
# Database configuration processed' app.confBloques de texto multilínea
Insertar bloques de configuración complejos:
sed '/^# SSL Configuration/i \
# SSL Certificate Setup\
ssl_certificate /path/to/cert.pem;\
ssl_certificate_key /path/to/key.pem;\
ssl_protocols TLSv1.2 TLSv1.3;' nginx.confAñadir datos estructurados:
sed '/^{$/a \
"name": "default",\
"version": "1.0",\
"active": true' config.jsonEl footer sed '$a END' de antes es un trabajo distinto: una línea al EOF, no un bloque estructurado. Para bloques JSON o YAML de cualquier tamaño real, prefiere jq o yq. Escapar datos estructurados multilínea a través del quoting de sed se vuelve frágil rápido.
Inserción de contenido dinámico
Usando variables y sustitución de comandos:
# Insert timestamp
current_time=$(date)
sed "1i\
Generated on: $current_time" report.txt
# Append system information
sed "\$a\
System: $(uname -s), User: $(whoami)" logfile.txtInsertar contenido de archivo con r
El comando r lee otro archivo a la salida después de la línea coincidente:
# Insert entire file content
sed '/INCLUDE_POINT/r include.txt' main.txt
# Combine with text insertion
sed '/HEADER/{
i\
# Configuration file begins
r config_template.txt
a\
# Configuration file ends
}' main.confsed r falla en silencio
Si el nombre de archivo no puede leerse, GNU sed lo trata como un archivo vacío sin indicación de error. Una ruta mal tecleada produce salida sin cambios y código de salida 0, indistinguible del éxito. Haz siempre grep -n tras usar r para confirmar que el contenido aterrizó.
POSIX también especifica que el archivo se lee “en el momento en que se escribe la salida”, no cuando el comando r se ejecuta. Para la salida normal de una pasada de sed eso rara vez importa, pero no asumas que el contenido se captura al momento de la coincidencia.
Operaciones con rangos de patrón
# Insert text before each line in a range
sed '10,20i\
# Line in middle section' filename
# Append after each line in a pattern-delimited range
sed '/START/,/END/a\
# Block processed' filenameRecuerda la semántica de rangos de antes: el comando se dispara para cada línea del rango, no una vez al inicio de él.
Técnicas avanzadas de scripting
Múltiples operaciones en secuencia:
sed -e '/function/i\
# Function definition' \
-e '/function/a\
# Function body starts' \
-e '/return/i\
# Function ends' \
-e '/return/a\
# Return statement processed' script.pyUsar scripts sed para operaciones complejas. Crea modify.sed:
/^# Database/i\
# Database Configuration Section
/^# Database/a\
host=localhost
/^# Database/a\
port=5432
/^# Security/i\
# Security Settings Section
/^# Security/a\
enable_ssl=trueEl estilo i\/a\ + newline es lo que quieres en archivos de script. Evita los casos extremos que varían entre implementaciones de sed.
Córrelo con:
sed -f modify.sed config.ini-f es el movimiento correcto una vez que un programa sed crece más allá de lo que puedes leer en una línea. Checkea el script junto al código que modifica.
Ejemplos avanzados prácticos
Procesamiento de archivos de log:
now="$(date)"
sed -e '/ERROR/i\
=== ERROR DETECTED ===' \
-e "/ERROR/a\
Timestamp: $now" \
-e '/WARN/i\
--- Warning ---' application.logInyección en el head HTML:
sed -e '1i\
<!DOCTYPE html>' \
-e '/<head>/a\
<meta charset="UTF-8">' \
-e '/<head>/a\
<meta name="viewport" content="width=device-width, initial-scale=1.0">' index.htmlFlags solo-GNU que vale conocer
Cuatro flags que se ganan su lugar en scripts de operador (todos GNU sed):
--sandbox: rechaza los comandose,wyr. Corre scripts sed no confiables con él.--follow-symlinks: solo con-i; edita a través de symlinks en vez de reemplazarlos. Necesario para archivos de/etcsymlinkeados. sed 4.10 arregló una race TOCTOU en este flag.-z/--null-data: modo delimitado por NUL, hace pareja con pipelinesfind -print0en nombres de archivo con espacios.-E: regex extendida, ahora estándar POSIX desde Issue 8 (2024). Prefiérelo sobre-ren cualquier cosa nueva.
Cuándo NO usar sed
Fronteras honestas, de alguien que ha limpiado scripts sed ingeniosos:
- Añadir una línea al EOF.
echo 'line' >> filees más simple, atómico vía O_APPEND, y funciona en archivos vacíos.sed '$a'no añade nada ahí y es más difícil de leer. - Ediciones de config idempotentes repetidas entre máquinas. Un one-liner sed con guard de grep funciona en una máquina. En una flota,
lineinfile/blockinfilede Ansible te da idempotencia, dry-run y diffs gratis. - Bloques grandes o estructurados (JSON, YAML, fragmentos HTML).
jq,yq, o una herramienta de templating real ganan a escapar comillas y backslashes a través del quoting de sed. Si el insert tiene más de tres líneas y algo en él está comillado, reconsidera.
Conclusión
sed insert (i) y sed append (a) son comandos pequeños que hacen un trabajo bien: poner texto en una posición exacta. La forma portable i\ + newline corre en todas partes incluyendo macOS, 1i y $a son no-ops silenciosos en archivos vacíos, y sed -i reemplaza tu archivo entre bambalinas. Previsualiza en stdout, guarda el .bak, verifica con diff.
- Usa
i\+ newline para cualquier script que deba correr en macOS o BSD 1iy$ano hacen nada en archivos vacíos. Usacat -o>>ahísed -ireemplaza el archivo (el inodo cambia, los hard links quedan rancios)- sed sale con 0 coincida o no. Guarda con
grep -qpara scripts idempotentes - Verifica con
diff file.bak fileygrep -nantes de borrar el backup
Más comandos sed que vale conocer:
- Borrar líneas con sed para quitar contenido no deseado
- Cambiar mayúsculas con sed para estandarizar capitalización
- Sed buscar y reemplazar para sustitución basada en patrones
Guía de referencia rápida
Comandos comunes de insert
# Insert at specific positions
sed '1i\
Header text' file.txt # Before first line
sed '5i\
Middle text' file.txt # Before line 5
sed '/pattern/i\
Before text' file.txt # Before matching lines
# Multi-line insert (portable)
sed '1i\
Line 1\
Line 2\
Line 3' file.txt
# One-liner: GNU/BusyBox only, fails on macOS
sed '1i Header text' file.txtComandos comunes de append
# Append at specific positions
sed '5a\
After text' file.txt # After line 5
sed '$a\
Footer text' file.txt # After last line
sed '/pattern/a\
After text' file.txt # After matching lines
# Multi-line append (portable)
sed '$a\
Footer line 1\
Footer line 2\
Footer line 3' file.txt
# Change/replace a line
sed '5c\
Replacement text' file.txtPreguntas frecuentes
¿Cuál es la diferencia entre insert y append (y change)?
Insert (i) añade texto antes de la línea o coincidencia objetivo. Append (a) añade texto después. Change (c) descarta la línea objetivo y la reemplaza con el texto nuevo. Tras un insert, los números de línea posteriores bajan; append y change los dejan igual.
¿Cómo añado texto multilínea?
Usa la forma portable con backslash y un newline tras la letra del comando:
sed '1i\
Line 1\
Line 2' file.txtEsto funciona en GNU sed, BusyBox sed y BSD/macOS sed.
¿Puedo insertar variables en el texto?
Sí. Usa comillas dobles para que el shell las expanda, y prefiere la forma i\ + newline por seguridad:
sed "1i\\
Current date: $(date)" file.txt¿Cómo hago los cambios permanentes?
Usa edición in-place con un sufijo de backup:
# GNU sed (Linux)
sed -i.bak '1i\
Header' file.txt
# BSD sed (macOS)
sed -i '.bak' '1i\
Header' file.txtPuede que veas '1i\'$'\n''Header' en respuestas antiguas. Eso es quoting ANSI-C de bash/zsh, y no funciona en dash ni en sh puro. Mira en qué difieren bash, Zsh y fish para el porqué. Si quieres evitar las diferencias del flag -i por completo, usa la receta portable de archivo temporal de “Edición in-place segura”.
¿Qué pasa con los números de línea tras una inserción?
Insert: los números de línea debajo del punto de inserción bajan en uno por línea insertada. Append: los números de línea existentes se quedan igual, las líneas nuevas aparecen debajo. Change: el conteo de líneas no cambia a menos que el reemplazo sea multilínea.
¿Puedo insertar/añadir a múltiples archivos a la vez?
Con -i, sí: sed -i '1i Header' *.txt (forma GNU/BusyBox) pone el header en cada archivo, porque sed -i de GNU implica -s (streams separados por archivo). Sin -i, sed '$a END' f1 f2 trata los archivos como un solo stream y añade solo tras el último archivo. -s en sí es solo-GNU. La respuesta portable es un bucle por archivo. Mira el gotcha de “Múltiples archivos” de arriba.
¿Por qué sed no hace nada en un archivo vacío?
1i y $a son no-ops silenciosos sobre entrada vacía: sed nunca corre un ciclo, así que el texto encolado nunca se escribe. Código de salida 0, sin error. Preppend con printf 'Header\n' | cat - file.txt y append con echo 'Footer' >> file.txt en su lugar.
¿Cómo sé que mi edición ocurrió? ¿Por qué mi script insertó la línea dos veces?
sed sale con 0 coincida o no la dirección, así que $? no te dice nada sobre si se insertó texto. Comprueba con grep -n 'NEW TEXT' file. Para scripts que corren más de una vez, guarda la edición:
grep -q '^PermitRootLogin' /etc/ssh/sshd_config || \
sed -i '/^#PermitRootLogin/a\
PermitRootLogin no' /etc/ssh/sshd_config¿Por qué sed 'r file' no insertó nada?
Si el nombre de archivo falta o es ilegible, GNU sed lo trata como un archivo vacío sin indicación de error. El comando “tiene éxito” y no inserta nada. Verifica que la ruta existe primero, y haz grep -n a la salida después.
¿Por qué mi hard link sigue mostrando contenido viejo tras sed -i?
sed -i escribe un archivo temporal y lo renombra sobre tu ruta. El inodo cambia, así que las rutas hermanas hard-linkeadas siguen apuntando al inodo viejo sin editar. Los symlinks se reemplazan por archivos regulares a menos que pases --follow-symlinks. Para preservar inodos y hard links, copia el contenido nuevo sobre el original en vez de renombrar: sed ... file > tmp && cat tmp > file.
¿Todo esto funciona en macOS y en imágenes Docker Alpine?
La forma portable i\ + newline funciona en macOS (BSD sed), Linux (GNU sed) e imágenes Alpine (BusyBox sed). La forma de una línea i text funciona en GNU y BusyBox pero falla en macOS. La sintaxis de edición in-place difiere (-i vs -i ''). Mira “¿Qué sed estás corriendo?” arriba para la comparación completa.


