vi en Linux — teoría con los tres modos, órdenes que se combinan como palabras y :%s (y el día que no sabía salir del editor)
Primera sesión del laboratorio de redes. El enunciado decía algo tan simple como «pon ONBOOT=yes en el archivo de la tarjeta y guarda». Abrí el archivo con vi, empecé a teclear ONBOOT=yes… y arriba del todo apareció una línea nueva que decía NBOOT=yes. Intenté salir con :q y la pantalla me contestó algo así (lo he reproducido después, en limpio):
ONBOOT=no IPADDR=192.168.2.27 NETMASK=255.255.255.0 GATEWAY=192.168.2.1 ~ ~ ~ ~ E37: No write since last change (add ! to override) Press ENTER or type command to continue
Ni guardaba, ni salía, ni entendía por qué. Acabé cerrando la terminal entera. La lección de aquel día: vi no es un editor raro, es un editor con modos, y en cuanto entiendes en qué modo estás, todo encaja.
En el artículo anterior escribimos scripts que automatizan la terminal. Hoy toca el editor que vas a encontrar en cualquier máquina Linux, aunque no tenga ni ratón ni ventanas: vi. Vamos a usar un archivo de verdad del laboratorio, ifcfg-enp2s0, la configuración de una tarjeta de red. Cuando acabes, tienes la práctica de vi para soltarte los dedos.
Tabla de Contenidos
Por qué vi (y por qué vim)
En el laboratorio de IC2 trabajas en máquinas con CentOS por terminal: no hay gedit ni bloc de notas. Pero vi siempre está: forma parte del estándar POSIX, así que cualquier Unix lo trae. Lo que hoy abres al escribir vi suele ser vim («vi mejorado»), que añade deshacer ilimitado, resaltado y mensajes más claros, pero respeta todas las órdenes de vi.
Todo lo que ves en este artículo es real: he tecleado cada secuencia en vim 9.1 dentro de una terminal de 72 columnas, con los ajustes por defecto de vim, y he copiado la pantalla tal y como quedó. Este es el archivo con el que vamos a trabajar:
$ cat -n ifcfg-enp2s0
1 DEVICE=enp2s0
2 BOOTPROTO=dhcp
3 ONBOOT=no
4 IPADDR=192.168.2.27
5 NETMASK=255.255.255.0
6 GATEWAY=192.168.2.1
Seis líneas con formato CLAVE=valor. Tiene dos cosas que habrá que arreglar (dhcp y ONBOOT=no), pero de momento solo vamos a aprender a movernos por él.
cat -n enseña el archivo con el número de cada línea. Lo uso antes y después de cada prueba para que veas exactamente qué cambió en el disco, no solo en la pantalla de vi.
Los tres modos de vi
Este es el concepto que lo cambia todo. vi tiene tres modos y cada tecla significa una cosa distinta según el modo en el que estés:
Fíjate en el dibujo: el modo normal está en el centro y Esc siempre te devuelve a él. Si alguna vez te pierdes, pulsa Esc (dos veces no hace daño) y ya sabes dónde estás.
| Modo | Para qué sirve | Cómo entras | Qué ves abajo |
|---|---|---|---|
| Normal | moverte, borrar, copiar, pegar | al abrir vi, o con Esc | nada, o el último mensaje |
| Inserción | escribir texto | i, a, o, A, O… | -- INSERT -- |
| Órdenes | guardar, salir, sustituir | : desde el modo normal | lo que escribes tras los dos puntos |
Vamos a recorrer los tres con este archivo de teclas. La notación es la de la documentación de vim: <Esc> es la tecla Escape y <CR> es Enter.
# Recién abierto: modo normal jjA # baja dos líneas y entra en inserción al final de la línea <Esc>:wq # vuelve a normal y empieza una orden (sin pulsar Enter) <Esc>:q<CR> # cancela la orden y sale: no habíamos cambiado nada
Nada más abrir, vi está en modo normal y en la última línea te dice qué archivo ha cargado:
DEVICE=enp2s0 BOOTPROTO=dhcp ONBOOT=no IPADDR=192.168.2.27 NETMASK=255.255.255.0 GATEWAY=192.168.2.1 ~ ~ ~ "ifcfg-enp2s0" 6L, 101B 1,1 All
6L, 101B significa 6 líneas y 101 bytes. Los ~ del final no son parte del archivo: marcan líneas «vacías» de la pantalla, más allá del final. Abajo a la derecha, 1,1 es la posición del cursor (línea 1, columna 1) y All dice que el archivo entero cabe en pantalla.
Tras jjA (dos líneas abajo y A = añadir al final de la línea) estamos en inserción:
DEVICE=enp2s0 BOOTPROTO=dhcp ONBOOT=no IPADDR=192.168.2.27 NETMASK=255.255.255.0 GATEWAY=192.168.2.1 ~ ~ ~ -- INSERT -- 3,10 All
El -- INSERT -- es tu mejor amigo: si lo ves, lo que teclees se escribe en el archivo. La posición 3,10 es justo detrás de ONBOOT=no (9 caracteres, así que la columna 10 es la siguiente).
Por último, Esc nos devuelve al modo normal y : abre el modo de órdenes. Lo que escribes aparece en la última línea, todavía sin ejecutar:
DEVICE=enp2s0 BOOTPROTO=dhcp ONBOOT=no IPADDR=192.168.2.27 NETMASK=255.255.255.0 GATEWAY=192.168.2.1 ~ ~ ~ :wq
Si pulsáramos Enter se guardaría y saldría. En vez de eso, Esc cancela la orden y :q sale sin tocar nada (no habíamos cambiado ni un carácter, así que vi no protesta). El archivo sigue igual:
$ cat -n ifcfg-enp2s0
1 DEVICE=enp2s0
2 BOOTPROTO=dhcp
3 ONBOOT=no
4 IPADDR=192.168.2.27
5 NETMASK=255.255.255.0
6 GATEWAY=192.168.2.1
Empezar a escribir nada más abrir vi. Estás en modo normal y cada letra es una orden: d, x, o, u… hacen cosas. Antes de escribir texto, pulsa i y busca el -- INSERT --.
Moverse sin ratón
En modo normal te mueves con el teclado. Las cuatro básicas son h (izquierda), j (abajo), k (arriba) y l (derecha): están en la fila central para no mover la mano. Las flechas también funcionan en vim, pero los saltos grandes son los que te ahorran tiempo:
| Tecla | Adónde va |
|---|---|
h j k l | un carácter a la izquierda, una línea abajo, una arriba, un carácter a la derecha |
w / b | al principio de la palabra siguiente / anterior |
0 / $ | al principio / al final de la línea |
f= | al siguiente = de la línea (vale con cualquier carácter: f., fy…) |
gg / G | a la primera / a la última línea |
5G | a la línea 5 |
3j | tres líneas abajo (un número delante repite el movimiento) |
/texto + Enter | a la siguiente aparición de texto |
n / N | a la siguiente / anterior aparición de la última búsqueda |
He tecleado este recorrido y he apuntado, tras cada tecla, la posición que marcaba vim en la esquina (la «regla», línea,columna):
j # una línea abajo $ # al final de la línea 0 # al principio de la línea f= # salta al siguiente = de la línea w # a la siguiente palabra G # a la última línea gg # a la primera línea 3j # tres líneas abajo /255<CR> # busca 255 hacia delante n # siguiente coincidencia :q<CR>
Esta es la traza real, con el carácter que quedaba bajo el cursor:
| Paso | Tecla | Posición | Carácter | Por qué |
|---|---|---|---|---|
| 1 | j | 2,1 | B | una línea abajo, misma columna |
| 2 | $ | 2,14 | p | BOOTPROTO=dhcp tiene 14 caracteres |
| 3 | 0 | 2,1 | B | vuelta al principio |
| 4 | f= | 2,10 | = | BOOTPROTO tiene 9 letras: el = es el 10 |
| 5 | w | 2,11 | d | para vi, = y dhcp son «palabras» distintas |
| 6 | G | 6,1 | G | última línea (la G de GATEWAY) |
| 7 | gg | 1,1 | D | primera línea |
| 8 | 3j | 4,1 | I | 1 + 3 = 4 |
| 9 | /255 Enter | 5,9 | 2 | NETMASK= ocupa 8 caracteres |
| 10 | n | 5,13 | 2 | el segundo 255: 9 + 4 (255.) = 13 |
Y aquí lo tienes dibujado sobre el archivo:
Mira el paso 5: w no salta a la línea siguiente, sino de = a dhcp, porque los signos de puntuación cuentan como palabras propias. Y en el 10, n repite la búsqueda sin volver a escribirla.
/ busca hacia delante y ? hacia atrás. Si la búsqueda llega al final, vim da la vuelta y sigue desde el principio (te avisa con search hit BOTTOM, continuing at TOP).
Cambiar texto: i, a, o, x y cw
Para escribir hay varias puertas al modo inserción. Todas hacen lo mismo (te dejan escribir), lo que cambia es dónde empiezas:
| Tecla | Empiezas a escribir… |
|---|---|
i / a | antes / después del cursor |
I / A | al principio / al final de la línea |
o / O | en una línea nueva debajo / encima |
cw | en lugar de la palabra (la borra y entra en inserción) |
cc | en lugar de la línea entera |
Y para pequeños arreglos sin entrar en inserción: x borra el carácter del cursor y r seguido de una letra lo reemplaza (rY cambia lo que haya por Y).
Con esto ya podemos arreglar la primera cosa del archivo: dhcp debe ser none (la IP va escrita a mano, no la da un servidor DHCP).
/dhcp<CR> # el cursor salta a la d de dhcp cwnone<Esc> # cambia la palabra entera por none u # deshace el cambio <C-r> # lo rehace :wq<CR> # guarda y sale
/dhcp deja el cursor sobre la d y cw sustituye la palabra entera. Tras Esc:
DEVICE=enp2s0
BOOTPROTO=none
ONBOOT=no
IPADDR=192.168.2.27
NETMASK=255.255.255.0
GATEWAY=192.168.2.1
~
~
~
2,14 All
El cursor quedó en 2,14, sobre la última letra de lo escrito: al salir de inserción, vi retrocede una posición. Ahora u (undo) deshace el último cambio:
DEVICE=enp2s0 BOOTPROTO=dhcp ONBOOT=no IPADDR=192.168.2.27 NETMASK=255.255.255.0 GATEWAY=192.168.2.1 ~ ~ ~ 1 change; before #1 2 seconds ago 2,11 All
Vuelve dhcp y vim te cuenta qué ha deshecho: 1 change; before #1 (deshizo 1 cambio, el número 1 de la historia) y hace cuánto se hizo (en tu ordenador saldrá otro tiempo). Ctrl-r (redo) lo rehace, y :wq guarda. Resultado en el disco:
$ cat -n ifcfg-enp2s0
1 DEVICE=enp2s0
2 BOOTPROTO=none
3 ONBOOT=no
4 IPADDR=192.168.2.27
5 NETMASK=255.255.255.0
6 GATEWAY=192.168.2.1
cw es c (cambiar) + w (hasta la siguiente palabra). No es una orden suelta que haya que memorizar: es una combinación. Eso es lo que vemos ahora.
Borrar, copiar y pegar: la gramática de vi
Aquí está la idea que hace que vi deje de parecer una lista infinita de atajos. Las órdenes se construyen como frases:
- Operador:
d(delete, borrar),c(change, cambiar),y(yank, copiar). - Movimiento: cualquiera de la sección anterior (
w,$,j,G…). - Número delante: cuántas veces.
Si repites el operador (dd, cc, yy), actúa sobre la línea entera. Lo que borras o copias va a un «portapapeles» de vi (el registro sin nombre) y lo pegas con p (debajo o detrás del cursor) o P (encima o delante).
| Quiero… | Orden | Se lee |
|---|---|---|
| borrar la línea | dd | borrar línea |
| borrar 3 líneas | 3dd | 3 veces borrar línea |
| borrar hasta el final de línea | d$ | borrar hasta $ |
| copiar la línea | yy | copiar línea |
| pegar debajo / encima | p / P | |
| deshacer / rehacer | u / Ctrl-r | |
| repetir el último cambio | . |
Probémoslo:
4G # va a la línea 4 (IPADDR) yyp # copia la línea y la pega debajo dd # borra la copia gg3dd # vuelve arriba y borra tres líneas u # deshace el borrado de las tres :q!<CR> # sale sin guardar nada
4G nos lleva a la línea de IPADDR; yy la copia y p la pega debajo:
DEVICE=enp2s0
BOOTPROTO=dhcp
ONBOOT=no
IPADDR=192.168.2.27
IPADDR=192.168.2.27
NETMASK=255.255.255.0
GATEWAY=192.168.2.1
~
~
5,1 All
Ahora hay 7 líneas y el cursor está en la copia (5,1). dd la borra; gg sube a la primera y 3dd borra tres de golpe:
IPADDR=192.168.2.27 NETMASK=255.255.255.0 GATEWAY=192.168.2.1 ~ ~ ~ ~ ~ ~ 3 fewer lines 1,1 All
3 fewer lines: las líneas 1, 2 y 3 han desaparecido y quedan 6 − 3 = 3. Un u lo deshace entero, porque 3dd es un solo cambio:
DEVICE=enp2s0 BOOTPROTO=dhcp ONBOOT=no IPADDR=192.168.2.27 NETMASK=255.255.255.0 GATEWAY=192.168.2.1 ~ ~ ~ 3 more lines; before #3 1 second ago 1,1 All
Para terminar, :q! sale sin guardar: todo lo que hicimos se queda en la memoria de vi y el disco no se entera.
$ cat -n ifcfg-enp2s0
1 DEVICE=enp2s0
2 BOOTPROTO=dhcp
3 ONBOOT=no
4 IPADDR=192.168.2.27
5 NETMASK=255.255.255.0
6 GATEWAY=192.168.2.1
Vi trabaja sobre una copia del archivo en memoria (el búfer). Nada llega al disco hasta que escribes :w. Por eso :q! es tan útil: es el botón de «aquí no ha pasado nada».
Buscar y sustituir
Para buscar ya conoces /texto, n y N. Para sustituir, el modo de órdenes tiene :s:
| Orden | Qué cambia |
|---|---|
:s/viejo/nuevo/ | la primera aparición en la línea del cursor |
:s/viejo/nuevo/g | todas las de la línea del cursor |
:%s/viejo/nuevo/g | todas las de todo el archivo (% = todas las líneas) |
:2,4s/viejo/nuevo/g | todas las de las líneas 2 a 4 |
Imagina que el laboratorio cambia de red: la estación pasa de 192.168.2.x a 10.0.1.x. Hay dos líneas que lo llevan:
:%s/192.168.2/10.0.1/g<CR> :wq<CR>
Así queda la pantalla justo después de pulsar Enter:
DEVICE=enp2s0 BOOTPROTO=dhcp ONBOOT=no IPADDR=10.0.1.27 NETMASK=255.255.255.0 GATEWAY=10.0.1.1 ~ ~ ~ :%s/192.168.2/10.0.1/g 6,1 All
La orden se queda escrita abajo y el archivo ya muestra 10.0.1.27 y 10.0.1.1. Tras :wq:
$ cat -n ifcfg-enp2s0
1 DEVICE=enp2s0
2 BOOTPROTO=dhcp
3 ONBOOT=no
4 IPADDR=10.0.1.27
5 NETMASK=255.255.255.0
6 GATEWAY=10.0.1.1
Olvidar que el patrón es una expresión regular. En 192.168.2 cada . significa «cualquier carácter», así que también encajaría con 192x168y2. Aquí no pasa nada, pero si quieres un punto de verdad, escríbelo como \.. Lo verás en la práctica.
Guardar y salir (sin quedarte atrapado)
El gran meme de vi. Estas son todas las salidas que necesitas:
| Orden | Qué hace |
|---|---|
:w | guarda y sigues en vi |
:q | sale, solo si no hay cambios sin guardar |
:wq (o :x, o ZZ en modo normal) | guarda y sale |
:q! | sale tirando los cambios |
:w otro-nombre | guarda una copia con otro nombre |
¿Qué pasa si hay cambios y pruebas con :q? Esto:
dd # un cambio cualquiera :q<CR> # intenta salir <CR> # Enter para seguir :q!<CR> # sale tirando el cambio
El dd borra la primera línea y el :q choca con esto:
ONBOOT=no IPADDR=192.168.2.27 NETMASK=255.255.255.0 GATEWAY=192.168.2.1 ~ ~ ~ ~ E37: No write since last change (add ! to override) Press ENTER or type command to continue
E37: No write since last change (add ! to override): «no has guardado desde el último cambio (añade ! para forzar)». vi te protege: no te deja perder el trabajo sin querer. El mensaje es largo y la pantalla sube una línea, por eso aparece Press ENTER: pulsas Enter y vuelves al modo normal. Ahí eliges: :wq si quieres guardar, :q! si no. Nosotros elegimos :q!, así que el dd nunca llegó al disco:
$ cat -n ifcfg-enp2s0
1 DEVICE=enp2s0
2 BOOTPROTO=dhcp
3 ONBOOT=no
4 IPADDR=192.168.2.27
5 NETMASK=255.255.255.0
6 GATEWAY=192.168.2.1
Si ves una pantalla rara y no sabes qué has hecho, la salida de emergencia es Esc, Esc, :q! y Enter. Sales sin guardar nada y vuelves a abrir con calma.
Errores típicos
Cada letra es una orden. ONBOOT=yes tecleado sin pulsar i antes abre una línea encima (la O) y escribe NBOOT=yes. Busca siempre el -- INSERT --.
Sale E37. No es un fallo: decide entre :wq y :q!.
:s/a/b/g solo toca la línea del cursor. Para todo el archivo es :%s/a/b/g.
:%s/0/1/ cambia solo el primer 0 de cada línea.
Los ifcfg de /etc son de root. Si abres uno como usuario normal, vim te avisa de que es de solo lectura y :w falla. Ábrelo con sudo vi (lo verás en el tema de red).
Pruébalo en tu terminal
Haz una copia para trastear sin miedo y repite esta sesión. Yo la llamé ifcfg-test, con el mismo contenido que el archivo de antes:
$ vi ifcfg-test
Dentro de vi, teclea esto (nada de inserción: todo en modo normal):
dd # borra la primera línea dd # y la siguiente u # deshace un borrado :q!<CR> # sale sin guardar
Tras los dos dd deberías ver:
ONBOOT=no
IPADDR=192.168.2.27
NETMASK=255.255.255.0
GATEWAY=192.168.2.1
~
~
~
~
~
1,1 All
Y tras el u:
BOOTPROTO=dhcp ONBOOT=no IPADDR=192.168.2.27 NETMASK=255.255.255.0 GATEWAY=192.168.2.1 ~ ~ ~ ~ 1 more line; before #2 1 second ago 1,1 All
Al salir con :q!, comprueba que el archivo no ha cambiado:
$ cat -n ifcfg-test
1 DEVICE=enp2s0
2 BOOTPROTO=dhcp
3 ONBOOT=no
4 IPADDR=192.168.2.27
5 NETMASK=255.255.255.0
6 GATEWAY=192.168.2.1
Qué observar:
- Tras el primer
dd, la línea 2 sube a ser la 1: vi no deja huecos, recoloca las líneas. - El segundo
ddborra la que ahora es la primera (BOOTPROTO=dhcp), no la línea 2 original. udeshace solo el último borrado: vuelveBOOTPROTO, peroDEVICEsigue fuera. El mensaje dicebefore #2porque deshace el cambio número 2.:q!tira los dos cambios a la vez: en el disco no ha pasado nada, como demuestracat -n.- Repite la prueba con
:wqen lugar de:q!y vuelve a hacercat -n: ahora sí falta la línea.
Resumen
| Concepto | Lo esencial |
|---|---|
| Modos | normal (órdenes), inserción (texto), órdenes (:); Esc vuelve a normal |
| Moverse | h j k l, w b, 0 $, gg G, 5G, f=, /texto, n |
| Escribir | i a I A o O, cw, cc; x y r para arreglos rápidos |
| Gramática | número + operador (d c y) + movimiento: 3dd, dw, cw, 2yy |
| Pegar y deshacer | p P, u, Ctrl-r, . |
| Sustituir | :s/a/b/ (línea), :%s/a/b/g (todo, todas las veces) |
| Salir | :w, :wq, :q, :q!, ZZ |
Tres ideas para llevarte:
- Antes de teclear nada, pregúntate en qué modo estás. Esc te devuelve siempre al normal.
- Las órdenes son frases: operador + movimiento, con un número delante si quieres repetir.
- vi edita una copia en memoria:
:wla lleva al disco y:q!la tira.
Ahora te toca a ti: en la práctica de vi dejamos el ifcfg-enp2s0 listo para el laboratorio con tres ediciones completas. Y con vi dominado, el siguiente paso es lo que hay dentro de ese archivo: la configuración de red en Linux.
¿Te ha servido este artículo? Así puedes apoyar SergioLearns →




Qué es un sistema operativo — teoría con tu portátil en una tarde de estudio, una llamada a write() y la CPU que nunca descansa (y los tres programas que no se ejecutaban a la vez)