<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fede</title>
    <description>The latest articles on DEV Community by Fede (@fefeco).</description>
    <link>https://dev.to/fefeco</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1082398%2F8841cf35-37a6-4588-80b7-092063caaa37.png</url>
      <title>DEV Community: Fede</title>
      <link>https://dev.to/fefeco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fefeco"/>
    <language>en</language>
    <item>
      <title>Escribir Caracteres del Español con Teclado Inglés en Linux: Hackeando XKB sin Permisos de Root</title>
      <dc:creator>Fede</dc:creator>
      <pubDate>Wed, 12 Aug 2026 17:41:25 +0000</pubDate>
      <link>https://dev.to/fefeco/escribir-caracteres-del-espanol-con-teclado-ingles-en-linux-hackeando-xkb-sin-permisos-de-root-48h8</link>
      <guid>https://dev.to/fefeco/escribir-caracteres-del-espanol-con-teclado-ingles-en-linux-hackeando-xkb-sin-permisos-de-root-48h8</guid>
      <description>&lt;p&gt;Si te dedicas a la programación o infraestructura en Linux, sabrás que la distribución física de un teclado en inglés suele ser superior para escribir código o el manejo de la terminal. Caracteres clave como las comillas &lt;code&gt;"&lt;/code&gt;, los paréntesis &lt;code&gt;()&lt;/code&gt;, llaves &lt;code&gt;{}&lt;/code&gt; y corchetes &lt;code&gt;[]&lt;/code&gt; están mejor ubicados que en los teclados en español, sin mencionar lo incómodo que resulta en estos últimos encontrar la virgulilla &lt;code&gt;~&lt;/code&gt; para hacer referencia al &lt;em&gt;home&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Sin embargo, como hispanohablantes el tener el teclado con el layout en inglés nos impide escribir fácilmente caracteres acentuados o la letra eñe si tenemos que redactar correos, mantener conversaciones por chat, etc.&lt;/p&gt;

&lt;p&gt;Frente a esta carencia, la solución tradicional suele ser activar variantes internacionales con &lt;strong&gt;teclas muertas&lt;/strong&gt; (&lt;em&gt;dead keys&lt;/em&gt;), pero trae complicaciones severas al programar. Por ejemplo, al intentar escribir un &lt;em&gt;string&lt;/em&gt; (&lt;code&gt;'api_key'&lt;/code&gt;), pulsar &lt;code&gt;'&lt;/code&gt; seguido de &lt;code&gt;a&lt;/code&gt; inserta &lt;code&gt;á&lt;/code&gt;. Entonces, para conseguir el símbolo suelto &lt;code&gt;'&lt;/code&gt;, debes presionar &lt;code&gt;'&lt;/code&gt; y luego la barra espaciadora para "destrabar la tecla muerta".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;¿Por que las llaman teclas muertas?&lt;/strong&gt;&lt;br&gt;
Es un mecanismo heredado de las &lt;strong&gt;máquinas de escribir mecánicas&lt;/strong&gt;. En ellas, al pulsar la tecla del acento (como &lt;code&gt;´&lt;/code&gt; o &lt;code&gt;¨&lt;/code&gt;), se imprimía la marca en el papel pero &lt;strong&gt;el carro no avanzaba&lt;/strong&gt; ("muerta" porque no movía el papel). Al pulsar la siguiente vocal, esta se imprimía justo en la misma posición. Al llevar esta modalidad a los ordenadores, teclas como &lt;code&gt;'&lt;/code&gt;, &lt;code&gt;"&lt;/code&gt;, &lt;code&gt;`&lt;/code&gt; o &lt;code&gt;~&lt;/code&gt; quedan bloqueadas a la espera de una segunda pulsación.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Durante un tiempo, mi solución fue configurar el teclado inglés más un mapa de teclado en español (a pesar de tener el teclado físico en inglés) y alternar constantemente entre ambos presionando &lt;code&gt;Alt + Shift&lt;/code&gt; según estuviera en el editor de código o en aplicaciones de mensajería. Era un parche incómodo, propenso a errores y requería adivinar continuamente en qué modo estaba el teclado.&lt;/p&gt;

&lt;p&gt;La búsqueda de una solución limpia me llevó a descubrir que en la distribución estadounidense existe una joya llamada &lt;code&gt;us(altgr-intl)&lt;/code&gt; (EE. UU. AltGr Internacional). Con ella puedes escribir código normalmente (las comillas y &lt;em&gt;backticks&lt;/em&gt; responden al instante) y si mantienes presionado &lt;strong&gt;AltGr&lt;/strong&gt; + alguna vocal, obtienes acentos (&lt;code&gt;á&lt;/code&gt;, &lt;code&gt;é&lt;/code&gt;, &lt;code&gt;í&lt;/code&gt;, &lt;code&gt;ó&lt;/code&gt;, &lt;code&gt;ú&lt;/code&gt;), y con &lt;strong&gt;AltGr + n&lt;/strong&gt;, la &lt;code&gt;ñ&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;¿Por qué entonces escribir este post si ya he dado con la solución? Porque el portátil que tengo ahora tiene el teclado británico y esta distribución no tiene su equivalente nativo al &lt;code&gt;us(altgr-intl)&lt;/code&gt; entonces toca crear uno que me otorgue esta solución.&lt;/p&gt;




&lt;h2&gt;
  
  
  Anatomía de XKB: Dónde Residen los Teclados en Linux
&lt;/h2&gt;

&lt;p&gt;En la mayoría de las distribuciones de Linux, la gestión del teclado recae en el subsistema &lt;strong&gt;XKB&lt;/strong&gt; y la librería &lt;code&gt;libxkbcommon&lt;/code&gt;. Las definiciones de los mapas de teclado se encuentran centralizadas en el directorio del sistema &lt;code&gt;/usr/share/X11/xkb/symbols/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Dentro de este directorio, cada fichero representa una distribución base o &lt;em&gt;Layout&lt;/em&gt; por país o idioma, como &lt;code&gt;us&lt;/code&gt; (EE. UU.), &lt;code&gt;gb&lt;/code&gt; (Reino Unido) o &lt;code&gt;es&lt;/code&gt; (España), entre otros. A su vez, dentro de cada fichero se definen las distintas &lt;strong&gt;Variantes&lt;/strong&gt; (subconfiguraciones como &lt;code&gt;dvorak&lt;/code&gt;, &lt;code&gt;mac&lt;/code&gt; o &lt;code&gt;altgr-intl&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Editar directamente el archivo correspondiente a la distribución de nuestro teclado (en mi caso &lt;code&gt;/usr/share/X11/xkb/symbols/gb&lt;/code&gt;) parecería lo obvio para obtener el comportamiento deseado. Sin embargo, esto requiere permisos de superusuario y podríamos perder nuestra configuración, ya que cualquier actualización del paquete &lt;code&gt;xkeyboard-config&lt;/code&gt; sobrescribirá y borrará todos los cambios.&lt;/p&gt;

&lt;p&gt;Por suerte, &lt;code&gt;libxkbcommon&lt;/code&gt; implementa una jerarquía estricta de búsqueda para estos ficheros con un orden de prioridad:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Usuario (Prioridad Máxima):
   ~/.config/xkb/symbols/  (o $XDG_CONFIG_HOME/xkb/symbols/)
   ~/.xkb/symbols/         (Ruta legacy)

2. Sistema Global Custom (Prioridad Media):
   /etc/xkb/symbols/

3. Paquete del Sistema (Prioridad Baja):
   /usr/share/X11/xkb/symbols/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto significa que si colocamos dentro del directorio &lt;code&gt;~/.config/xkb/symbols/&lt;/code&gt; un archivo llamado &lt;code&gt;gb&lt;/code&gt;, el sistema lo evaluará como prioritario, cargándolo con preferencia absoluta sobre la versión del sistema.&lt;/p&gt;




&lt;h2&gt;
  
  
  Diseñando la Variante &lt;code&gt;altgr-intl&lt;/code&gt; en XKB para el layout de teclado británico
&lt;/h2&gt;

&lt;p&gt;La estructura de un fichero de definición XKB se organiza en bloques &lt;code&gt;xkb_symbols&lt;/code&gt; y dentro podemos mapear hasta 4 niveles de caracteres por tecla de la siguiente manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key &amp;lt;Cod_tecla&amp;gt; { [ Nivel 1, Nivel 2, Nivel 3, Nivel 4 ] };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Donde:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nivel 1:&lt;/strong&gt; Presionar la tecla sin más.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nivel 2:&lt;/strong&gt; Tecla + &lt;code&gt;Shift&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nivel 3:&lt;/strong&gt; Tecla + &lt;code&gt;AltGr&lt;/code&gt; (aquí es donde modificaremos su comportamiento).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nivel 4:&lt;/strong&gt; Tecla + &lt;code&gt;AltGr&lt;/code&gt; + &lt;code&gt;Shift&lt;/code&gt; (aquí también).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 ¿Cómo nombra XKB las teclas físicas?&lt;/strong&gt;&lt;br&gt;
XKB no identifica las teclas por la letra impresa en el teclado, sino por su posición (de abajo a arriba por filas &lt;code&gt;AA&lt;/code&gt; a &lt;code&gt;AE&lt;/code&gt;, y de izquierda a derecha por columnas &lt;code&gt;01&lt;/code&gt; a &lt;code&gt;XX&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fila AE (Números):&lt;/strong&gt; &lt;code&gt;&amp;lt;AE01&amp;gt;&lt;/code&gt; = 1, &lt;code&gt;&amp;lt;AE02&amp;gt;&lt;/code&gt; = 2...&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fila AD (Letras superiores):&lt;/strong&gt; &lt;code&gt;&amp;lt;AD01&amp;gt;&lt;/code&gt; = Q, &lt;code&gt;&amp;lt;AD02&amp;gt;&lt;/code&gt; = W, &lt;code&gt;&amp;lt;AD03&amp;gt;&lt;/code&gt; = E...&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fila AC (Home Row):&lt;/strong&gt; &lt;code&gt;&amp;lt;AC01&amp;gt;&lt;/code&gt; = A, ..., &lt;code&gt;&amp;lt;AC04&amp;gt;&lt;/code&gt; = F...&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fila AB (Letras inferiores):&lt;/strong&gt; &lt;code&gt;&amp;lt;AB01&amp;gt;&lt;/code&gt; = Z, ..., &lt;code&gt;&amp;lt;AB06&amp;gt;&lt;/code&gt; = N...&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fila AA (Ctrl, Alt, Espacio...):&lt;/strong&gt; &lt;code&gt;&amp;lt;AA01&amp;gt;&lt;/code&gt; = Ctrl Izquierdo, &lt;code&gt;&amp;lt;AA08&amp;gt;&lt;/code&gt; = Espacio... Aunque para estas teclas XKB tiene alias como  y  respectivamente.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para implementar nuestra variante manteniendo la herencia de &lt;code&gt;gb(basic)&lt;/code&gt;, tenemos dos opciones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Copiar el archivo base del sistema&lt;/strong&gt; &lt;code&gt;/usr/share/X11/xkb/symbols/gb&lt;/code&gt; a nuestra ruta local &lt;code&gt;~/.config/xkb/symbols/gb&lt;/code&gt; y añadir nuestro bloque &lt;code&gt;altgr-intl&lt;/code&gt; al final. De esta manera, la directiva &lt;code&gt;include "gb(basic)"&lt;/code&gt; encontrará la sección &lt;code&gt;basic&lt;/code&gt; definida dentro del mismo archivo local.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crear un archivo con nombre personalizado&lt;/strong&gt; y guardar nuestro bloque allí como &lt;code&gt;~/.config/xkb/symbols/gb_custom&lt;/code&gt;. Al llamarse distinto del archivo del sistema, XKB buscará la sección &lt;code&gt;basic&lt;/code&gt; directamente al fichero oficial &lt;code&gt;/usr/share/X11/xkb/symbols/gb&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La estructura de la nueva variante queda así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;partial alphanumeric_keys
xkb_symbols "altgr-intl" {
    // 1. Heredar la base del teclado británico estándar
    include "gb(basic)"

    name[Group1]= "English (UK, AltGr international)";

    // 2. Mapear acentos y la letra ñ al Nivel 3 (AltGr) y Nivel 4 (AltGr+Shift)
    key &amp;lt;AD03&amp;gt; { [ e, E, eacute, Eacute ] };
    key &amp;lt;AD07&amp;gt; { [ u, U, uacute, Uacute ] };
    key &amp;lt;AD08&amp;gt; { [ i, I, iacute, Iacute ] };
    key &amp;lt;AD09&amp;gt; { [ o, O, oacute, Oacute ] };
    key &amp;lt;AC01&amp;gt; { [ a, A, aacute, Aacute ] };

    key &amp;lt;AB06&amp;gt; { [ n, N, ntilde, Ntilde ] };

    // 3. Habilitar la tecla Alt Derecho (Right Alt) como conmutador de Nivel 3
    include "level3(ralt_switch)"
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Desglose técnico del código:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;partial alphanumeric_keys&lt;/code&gt;: Le indica a XKB que este bloque no define un teclado completo desde cero, sino una modificación parcial centrada en la sección alfanumérica del teclado.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include "gb(basic)"&lt;/code&gt;: Carga todo el mapeo físico estándar impreso en los teclados británicos para que no tengamos que declarar todas las teclas.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;key &amp;lt;AD03&amp;gt;&lt;/code&gt;: Accede a la 3ª tecla de la fila superior (la &lt;code&gt;E&lt;/code&gt;). Al definir &lt;code&gt;[ e, E, eacute, Eacute ]&lt;/code&gt;, asignamos la &lt;code&gt;é&lt;/code&gt; minúscula al pulsar &lt;code&gt;AltGr + e&lt;/code&gt;, y la &lt;code&gt;É&lt;/code&gt; mayúscula al pulsar &lt;code&gt;AltGr + Shift + e&lt;/code&gt;, dejando su comportamiento normal para los dos primeros niveles.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include "level3(ralt_switch)"&lt;/code&gt;: Indica a XKB que la tecla Alt Derecho (&lt;em&gt;Right Alt&lt;/em&gt;) actuará como selector del Nivel 3.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Activación en el Entorno
&lt;/h2&gt;

&lt;p&gt;Una vez creado el archivo en &lt;code&gt;~/.config/xkb/symbols/gb&lt;/code&gt; (o &lt;code&gt;gb_custom&lt;/code&gt;), el último paso es indicarle al compositor que cargue esta nueva variante.&lt;/p&gt;

&lt;p&gt;En mi caso, utilizo &lt;strong&gt;Hyprland&lt;/strong&gt; como gestor de ventanas, por lo que basta con añadir estas líneas en mi archivo de configuración:&lt;/p&gt;

&lt;h3&gt;
  
  
  En la configuración de Hyprland (&lt;code&gt;~/.config/hypr/hyprland.conf&lt;/code&gt;):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input {
    kb_layout = gb          # O "gb_custom" si usaste un archivo independiente
    kb_variant = altgr-intl
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al recargar Hyprland, mantengo el 100% de los símbolos impresos en mi teclado británico y gano acceso directo e instantáneo a tildes (&lt;code&gt;á, é, í, ó, ú&lt;/code&gt;) y a la &lt;code&gt;ñ&lt;/code&gt; simplemente combinando las teclas con &lt;strong&gt;AltGr&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Llévalo al Siguiente Nivel: Hacks de Teclado Personalizados
&lt;/h2&gt;

&lt;p&gt;Entender la estructura de &lt;code&gt;xkb_symbols&lt;/code&gt; te abre la puerta a adaptar cualquier teclado a tus necesidades específicas.&lt;br&gt;
Ten en cuenta que no es obligatorio definir siempre los 4 niveles: si solo necesitas una combinación con &lt;strong&gt;AltGr&lt;/strong&gt;, basta con declarar 3 elementos dentro de los corchetes &lt;code&gt;[ ]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Te dejo 4 ideas prácticas como ejemplos útiles:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Arreglar la Virgulilla (&lt;code&gt;~&lt;/code&gt;) en el Teclado Español
&lt;/h3&gt;

&lt;p&gt;En el teclado de España (&lt;code&gt;es&lt;/code&gt;), escribir la virgulilla &lt;code&gt;~&lt;/code&gt; exige presionar &lt;code&gt;AltGr + 4&lt;/code&gt; seguido de la barra espaciadora por actuar como tecla muerta. Puedes hacer que se imprima directamente con &lt;code&gt;AltGr + ñ&lt;/code&gt; declarando solo 3 niveles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key &amp;lt;AC10&amp;gt; { [ ñ, Ñ, asciitilde ] };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Descongelar la Comilla Invertida / Backtick (&lt;code&gt;`&lt;/code&gt;) en el Teclado Español
&lt;/h3&gt;

&lt;p&gt;Como programador, el &lt;em&gt;backtick&lt;/em&gt; &lt;code&gt;`&lt;/code&gt; es un operador crítico. En distribuciones en español actúa como tecla muerta para el acento grave (como en francés &lt;code&gt;à&lt;/code&gt;), obligándote a presionar la tecla y luego espacio. Puedes redefinirla para que imprima el &lt;em&gt;backtick&lt;/em&gt; directo en Nivel 3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Imprime ` de forma directa en Nivel 1, manteniendo Shift + tecla = ^ y AltGr + tecla = [
key &amp;lt;AD11&amp;gt; { [ grave, dead_circumflex, bracketleft ] };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(O si también deseas descongelar el circunflejo &lt;code&gt;^&lt;/code&gt; para que no sea tecla muerta)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key &amp;lt;AD11&amp;gt; { [ grave, asciicircum, bracketleft ] };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Símbolos de Monedas e Iconos Financieros (&lt;code&gt;€&lt;/code&gt;, &lt;code&gt;£&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;₿&lt;/code&gt;, &lt;code&gt;💰&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Si trabajas con plataformas internacionales o finanzas, puedes asignar símbolos monetarios en el Nivel 3 y emojis gráficos relacionados en el Nivel 4:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// AltGr + e -&amp;gt; € (Euro) | AltGr + Shift + e -&amp;gt; 💶 (Billete de Euro)
key &amp;lt;AD03&amp;gt; { [ e, E, EuroSign, U1F4B6 ] };

// AltGr + b -&amp;gt; ₿ (Bitcoin) | AltGr + Shift + b -&amp;gt; 💰 (Bolsa de Dinero)
key &amp;lt;AB05&amp;gt; { [ b, B, U20BF, U1F4B0 ] };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Emojis y Símbolos de Redacción Rápida
&lt;/h3&gt;

&lt;p&gt;Si redactas documentación con frecuencia y quieres evitar abrir un &lt;em&gt;picker&lt;/em&gt; de emojis o buscar símbolos especiales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// AltGr + s -&amp;gt; 🚀 (U1F680 Cohete) | AltGr + Shift + s -&amp;gt; 💡 (U1F4A1 Bombilla)
key &amp;lt;AC02&amp;gt; { [ s, S, U1F680, U1F4A1 ] };

// AltGr + m -&amp;gt; — (Raya / Guion largo de diálogo U2014)
key &amp;lt;AB07&amp;gt; { [ m, M, U2014 ] };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Se te ocurre alguna otra configuración útil? Compártela!&lt;/p&gt;

</description>
      <category>keyboard</category>
      <category>spanish</category>
      <category>linux</category>
      <category>xkb</category>
    </item>
    <item>
      <title>De comandos manuales a scripts dinámicos: Cómo simplifiqué la clonación de Windows en WinPE con DISM</title>
      <dc:creator>Fede</dc:creator>
      <pubDate>Fri, 10 Jul 2026 22:21:34 +0000</pubDate>
      <link>https://dev.to/fefeco/de-comandos-manuales-a-scripts-dinamicos-como-simplifique-la-clonacion-de-windows-en-winpe-con-dism-4j6j</link>
      <guid>https://dev.to/fefeco/de-comandos-manuales-a-scripts-dinamicos-como-simplifique-la-clonacion-de-windows-en-winpe-con-dism-4j6j</guid>
      <description>&lt;p&gt;¿Alguna vez te has encontrado en esa situación donde hay una herramienta que te resuelve un problema pero en tu empresa no está permitida?&lt;br&gt;
Ya sea porque usan otra alternativa, por políticas de seguridad o simplemente por decisiones corporativas, te ves obligado a adaptarte.&lt;/p&gt;

&lt;p&gt;Aunque al principio esto puede parecer frustrante (sobre todo cuando crees que tu herramienta habitual te haría el trabajo más rápido y cómodo), en realidad es una oportunidad de oro. Te obliga a aprender cómo funciona el nuevo entorno y da la excusa perfecta para programar tus propios scripts desde cero para automatizar y simplificar el trabajo diario.&lt;/p&gt;

&lt;p&gt;En mi caso, me enfrenté a la clásica tarea de infraestructura: clonar imágenes de discos y realizar despliegues a partir de preinstalaciones de Windows.&lt;br&gt;
En este entorno, la herramienta permitida era &lt;strong&gt;DISM&lt;/strong&gt;, que viene de forma nativa en la suite de Windows y no requiere instalación extra.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Si quieres ir directo al grano, puedes consultar y descargar los scripts completos en el &lt;a href="https://github.com/fefeco/dism-winpe-imaging-scripts" rel="noopener noreferrer"&gt;repositorio del proyecto en GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Aquí es donde empieza esta historia.&lt;/p&gt;


&lt;h2&gt;
  
  
  Despliegue Unificado: Menú Dinámico y Formateo en un solo Script
&lt;/h2&gt;

&lt;p&gt;La primera vez que me tocó hacer un deploy en el trabajo, el punto de partida fue un archivo de texto con una línea de comando extensa que los técnicos copiábamos y pegábamos directamente en la consola de &lt;strong&gt;WinPE&lt;/strong&gt;. Además, debíamos modificar manualmente el nombre de la imagen &lt;code&gt;.wim&lt;/code&gt; que queríamos aplicar.&lt;br&gt;
Para preparar el disco antes del despliegue, también contábamos con un bloque de instrucciones de &lt;code&gt;diskpart&lt;/code&gt; (limpiar volumen, crear tabla GPT, definir particiones EFI y Windows), que estaba almacenado en un archivo de texto independiente.&lt;/p&gt;

&lt;p&gt;Tener comandos dispersos y archivos de configuración sueltos por el disco de herramientas me generaba desconfianza.&lt;br&gt;
Si un archivo se borra u otro se desactualiza, el proceso se interrumpe y falla resultando en una pérdida de tiempo en tareas que suelen demorar bastante.&lt;/p&gt;

&lt;p&gt;Pensé: &lt;em&gt;"Esto tiene que poder hacerse mejor"&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;La solución fue crear &lt;code&gt;deploy.cmd&lt;/code&gt; para concentrar todo el despliegue en un único script autoportante que realiza tres tareas clave de forma secuencial:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Escaneo dinámico y menú interactivo
&lt;/h3&gt;

&lt;p&gt;Para evitar errores de tipeo al introducir el nombre de la imagen &lt;code&gt;.wim&lt;/code&gt;, el script barre el directorio donde se almacenan y las lista en un menú.&lt;br&gt;
La decición de hacerlo así fue para que sea escalable y dinámico permitiendo agregar imágenes al directorio sin necesidad de modificar el script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:: Escaneo dinámico de imágenes
set COUNT=0
for %%A in ("%IMG_DIR%\*.wim") do (
    set /a COUNT+=1
    set "WIM[!COUNT!]=%%~fA"
    set "WIM_NAME[!COUNT!]=%%~nA"
)

:: ... generación del menú interactivo ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este simple bucle &lt;code&gt;for&lt;/code&gt; detecta los archivos de un directorio y almacena las rutas en "arrays" simulados de Batch, generando opciones numeradas para que solo deba seleccionar una y el script se encargará del resto.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Nota de sintaxis:&lt;/em&gt; El uso de signos de exclamación (&lt;code&gt;!&lt;/code&gt;) en lugar del porcentaje (&lt;code&gt;%&lt;/code&gt;) tradicional de Batch se debe a la necesidad de habilitar la expansión retardada (&lt;code&gt;setlocal enabledelayedexpansion&lt;/code&gt;). Sin esto, Batch evaluaría las variables del menú al cargar el bucle en memoria y no en tiempo de ejecución, lo que rompería la lógica del índice dinámico.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Integración de DiskPart al vuelo
&lt;/h3&gt;

&lt;p&gt;En lugar de depender de un archivo de texto externo para particionar el disco, el propio script genera las instrucciones de &lt;code&gt;diskpart&lt;/code&gt; en el directorio temporal y las ejecuta en segundo plano:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set "TEMP_SCRIPT=%temp%\dp_script.txt"

(
  echo select disk %TARGET_DISK%
  echo clean
  echo convert gpt
  echo create partition efi size=200
  echo format quick fs=fat32 label="EFI"
  echo assign letter=S
  :: ... creación de particiones MSR y Windows ...
) &amp;gt; "%TEMP_SCRIPT%"

diskpart /s "%TEMP_SCRIPT%" &amp;gt;&amp;gt; "%LOG_FILE%" 2&amp;gt;&amp;amp;1
del "%TEMP_SCRIPT%"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo ejecutamos silenciosamente con &lt;code&gt;diskpart /s&lt;/code&gt; y registramos su salida en un archivo de log (&lt;code&gt;%LOG_FILE%&lt;/code&gt;) para diagnosticar posibles fallos.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Configuración automática de arranque UEFI
&lt;/h3&gt;

&lt;p&gt;Una vez que DISM vuelca la imagen seleccionada, el script ejecuta el paso final para que el sistema sea arrancable de forma automática sin intervención manual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bcdboot Z:\Windows /s S: /f UEFI &amp;gt;&amp;gt; "%LOG_FILE%" 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este comando escribe los archivos de arranque en la partición EFI (&lt;code&gt;S:&lt;/code&gt;) apuntando a la instalación de Windows que acabamos de aplicar en la partición primaria (&lt;code&gt;Z:&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Al concentrar todo en un único archivo de script, logramos un proceso autocontenido: el mismo archivo prepara el hardware, automatiza la selección de la imagen y configura el arranque UEFI.&lt;/p&gt;




&lt;h2&gt;
  
  
  El Siguiente Desafío: La Migración Masiva y el Script de Captura
&lt;/h2&gt;

&lt;p&gt;Tener un script de despliegue sólido fue un gran avance, pero el verdadero desafío apareció con una actualización masiva de discos HDD a SSD. &lt;/p&gt;

&lt;p&gt;Normalmente, un proceso de captura se hace una sola vez: se prepara un sistema de referencia, se captura la imagen base y se despliega tantas veces como haga falta. Sin embargo, en una migración de hardware uno a uno no se puede hacer eso. Cada equipo debe conservar intactos los datos, perfiles y controladores específicos de su usuario. Esto significaba realizar una captura y un despliegue individual por cada ordenador del parque.&lt;/p&gt;

&lt;p&gt;Al multiplicar el proceso por el número de máquinas a actualizar, cualquier fricción manual se vuelve crítica.&lt;br&gt;
Fue en este escenario donde nació &lt;code&gt;capture.cmd&lt;/code&gt; y donde me topé con una limitación de DISM al procesar perfiles: el tratamiento de los archivos de OneDrive almacenados en la nube.&lt;/p&gt;

&lt;p&gt;DISM no procesa correctamente los punteros o enlaces de archivos que solo residen en la nube de OneDrive. Al capturar la imagen, el comando finalizaba sin indicar ningún error. Sin embargo, al desplegar esa misma imagen en un nuevo ordenador, el proceso fallaba a mitad de camino y se suspendía. &lt;/p&gt;

&lt;p&gt;Esto me obligaba a montar la imagen capturada en otro ordenador para limpiarla a mano, o a repetir la captura borrando previamente los directorios de OneDrive de los perfiles afectados.&lt;/p&gt;

&lt;p&gt;La solución que encontré era usar el parámetro &lt;code&gt;/ConfigFile&lt;/code&gt; de DISM para excluir los ficheros dentro de las carpetas de OneDrive pero los comodines de exclusión estándar de DISM resultaron ser muy erráticos. Por ejemplo, una regla genérica como &lt;code&gt;Users/*/OneDrive*&lt;/code&gt; no solo excluía los archivos sincronizados en la nube, sino que también coincidía con ejecutables internos como &lt;code&gt;Users/usuario/AppData/Microsoft/OneDrive/OneDrive.exe&lt;/code&gt;, provocando fallos en el perfil al iniciar sesión tras el despliegue.&lt;/p&gt;

&lt;p&gt;Para resolverlo de forma limpia, la solución fue crear de manera dinámica el archivo de configuración &lt;code&gt;exclude.ini&lt;/code&gt; personalizado antes de iniciar la captura.&lt;/p&gt;
&lt;h3&gt;
  
  
  El código en acción: Generación de exclusiones al vuelo
&lt;/h3&gt;

&lt;p&gt;Este es el bloque que resuelve el problema en &lt;code&gt;capture.cmd&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(
echo [ExclusionList]
:: ... exclusiones estándar del sistema ...
for /d %%U in ("%WINDOWS_PARTITION%\Users\*") do (
    set "USER_NAME=%%~nxU"
    if /I not "!USER_NAME!"=="All Users" (
        for /d %%O in ("%%U\OneDrive*") do (
            echo \Users\!USER_NAME!\%%~nxO\*
        )
    )
)
) &amp;gt; "%TEMP%\exclude.ini"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ¿Cómo funciona este filtro dinámico?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Exclusiones del sistema estándar:&lt;/strong&gt; Primero escribimos en el archivo temporal las exclusiones clásicas de Windows (archivos de paginación, papelera, etc.) que no deben incluirse en una clonación limpia.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iteración de perfiles de usuario:&lt;/strong&gt; Mediante &lt;code&gt;for /d %%U&lt;/code&gt; barremos el directorio &lt;code&gt;Users&lt;/code&gt;. Guardamos el nombre de cada carpeta en &lt;code&gt;USER_NAME&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filtro de cuentas y búsqueda de OneDrive:&lt;/strong&gt; Omitimos cuentas del sistema como "All Users". Luego, buscamos directorios que comiencen con &lt;code&gt;OneDrive&lt;/code&gt; dentro del perfil de cada usuario (&lt;code&gt;%%U\OneDrive*&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rutas absolutas sin comodines destructivos:&lt;/strong&gt; Si se encuentra una carpeta de OneDrive para ese usuario específico, escribimos su ruta absoluta exacta (por ejemplo, &lt;code&gt;\Users\usuario\OneDrive - Empresa\*&lt;/code&gt;) en la sección de exclusiones.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;El peligro oculto de &lt;code&gt;/ConfigFile&lt;/code&gt; en DISM&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Un comportamiento crítico de DISM es que, al pasarle un archivo &lt;code&gt;/ConfigFile&lt;/code&gt; personalizado, &lt;strong&gt;se desactivan por completo sus exclusiones nativas por defecto&lt;/strong&gt;. Si no se declaran de nuevo a mano, archivos masivos de sistema como &lt;code&gt;pagefile.sys&lt;/code&gt;, &lt;code&gt;hiberfil.sys&lt;/code&gt; o carpetas de reciclaje se incluirán en la captura. Por ello, el script escribe primero estas exclusiones estándar al inicio del archivo temporal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Una vez generado el archivo en &lt;code&gt;%TEMP%\exclude.ini&lt;/code&gt;, llamamos a DISM pasándole este archivo como parámetro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dism /Capture-Image /ImageFile:"%DESTINATION%\%IMAGE_NAME%" /CaptureDir:%WINDOWS_PARTITION%\ /Name:"My Windows partition" /ConfigFile:"%TEMP%\exclude.ini" /CheckIntegrity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al terminar, el script elimina el archivo &lt;code&gt;.ini&lt;/code&gt; temporal. Esto garantizó capturas exitosas sin intervención manual en los perfiles de usuario y sin falsos positivos en las exclusiones.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusiones y Trade-offs
&lt;/h2&gt;

&lt;p&gt;A veces, las soluciones de infraestructura más robustas no requieren suites de software de terceros complejas ni dependencias pesadas. La combinación de scripts de consola de Batch y herramientas nativas como DISM y DiskPart nos permitió construir un flujo de clonación confiable, dinámico y robusto.&lt;/p&gt;

&lt;p&gt;Como en cualquier decisión de diseño técnico, hubo compromisos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ventajas:&lt;/strong&gt; Es universal, ligero y se ejecuta en cualquier entorno WinPE sin requerir red ni dependencias. Aunque PowerShell suele ser la primera opción para automatizar en Windows, muchas imágenes de arranque WinPE minimalistas omiten el soporte de PowerShell para ahorrar tamaño. Batch garantiza que el script funcione de forma nativa en cualquier consola de recuperación por básica que sea.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trade-offs:&lt;/strong&gt; La sintaxis de Batch es rígida, carece de un manejo de errores sofisticado y la manipulación de variables en bucles requiere habilitar la expansión retardada (&lt;code&gt;setlocal ENABLEDELAYEDEXPANSION&lt;/code&gt;), lo cual añade cierta complejidad al código.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esta automatización transformó un proceso manual propenso a fallos tipográficos en una tarea de selección de opciones en un menú, asegurando que cada clonación e instalación se realizara sobre un estado limpio y predecible.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;El código fuente completo de los scripts y las instrucciones de uso están disponibles en el &lt;a href="https://github.com/fefeco/dism-winpe-imaging-scripts" rel="noopener noreferrer"&gt;repositorio del proyecto en GitHub&lt;/a&gt;. ¡Las sugerencias, reportes de fallos y contribuciones son más que bienvenidos!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>windows</category>
      <category>devops</category>
      <category>sysadmin</category>
      <category>scripting</category>
    </item>
  </channel>
</rss>
