7 usos esenciales y prácticos del comando pegar en Linux

En un artículo anterior, hablamos sobre el comando cortar que se puede utilizar para extraer columnas de un archivo CSV o de datos de texto tabular.

ElpasteEl comando hace exactamente lo contrario: fusiona varios archivos de entrada para producir un nuevo archivo de texto delimitado a partir de ellos. Vamos a ver cómo utilizar eficazmente el comando pegar en Linux y Unix.

7 ejemplos prácticos del comando pegar en Linux

Si prefiere los videos, puede ver este video que explica los mismos ejemplos de comandos de pegado que se analizan en este artículo.

1. Pegar columnas

En su caso de uso más básico, elpasteEl comando toma N archivos de entrada y los une línea por línea en la salida.

Utilicé el comando bash printf para formatear la salida de los archivos de entrada en el siguiente ejemplo.

sh$ printf "%sn" {a..e} | tee letters
a
b
c
d
e

sh$ printf "%sn" {1..5} | tee digits
1
2
3
4
5

sh$ paste letters digits
a    1
b    2
c    3
d    4
e    5

Pero dejemos ahora las explicaciones teóricas para trabajar en un ejemplo práctico. Si ha descargado los archivos de muestra utilizados en el vídeo anterior, podrá ver que tengo varios archivos de datos correspondientes a las distintas columnas de una tabla:

sh$ head -3 *.csv
==> ACCOUNTLIB.csv <==
ACCOUNTLIB
TIDE SCHEDULE
VAT BS/ENC

==> ACCOUNTNUM.csv <==
ACCOUNTNUM
623477
445452

==> CREDIT.csv <==
CREDIT
<--- empty line
<--- empty line

==> DEBIT.csv <==
DEBIT
00000001615,00
00000000323,00

Es bastante fácil producir un archivo de texto delimitado por tabulaciones a partir de esos datos:

sh$ paste *.csv | head -3
ACCOUNTLIB    ACCOUNTNUM    CREDIT    DEBIT
TIDE SCHEDULE    623477        00000001615,00
VAT BS/ENC    445452        00000000323,00

Como puede ver, cuando se muestra en la consola, el contenido de ese archivo de valores separados por tabulaciones no produce una tabla perfectamente formateada. Pero esto es por diseño: elpasteEl comando no se utiliza para crear archivos de texto de ancho fijo, sino solo archivos de texto delimitados donde a un carácter determinado se le asigna la función de ser el separador de campos.

Entonces, incluso si no es obvio en el resultado anterior, en realidad hay uno y solo un carácter de tabulación entre cada campo. Hagámoslo evidente usando el comando sed:

sh$ paste *.csv | head -3 | sed -n l
ACCOUNTLIBtACCOUNTNUMtCREDITtDEBIT$
TIDE SCHEDULEt623477tt00000001615,00$
VAT BS/ENCt445452tt00000000323,00$

Ahora, los caracteres invisibles se muestran sin ambigüedades en el resultado. Y puedes ver los caracteres de tabulación mostrados comot. Puedes contarlas: siempre hay tres pestañas en cada línea de salida, una entre cada campo. Y cuando ves dos de ellos seguidos, eso sólo significa que había un campo vacío allí. Este suele ser el caso en mis archivos de ejemplo particulares, ya que en cada línea se establece el campo CRÉDITO o DÉBITO, pero nunca ambos al mismo tiempo.

2. Cambiar el delimitador de campo

Como lo hemos visto, elpasteEl comando utiliza el carácter de tabulación como separador de campo predeterminado (“delimitador”). Algo que podemos cambiar usando el-dopción. Digamos que me gustaría usar un punto y coma en su lugar:

# The quotes around the ';' are used to prevent the
# shell to consider that semi-colon as being a command separator
sh$ paste -d ';' *.csv | head -3
ACCOUNTLIB;ACCOUNTNUM;CREDIT;DEBIT
TIDE SCHEDULE;623477;;00000001615,00
VAT BS/ENC;445452;;00000000323,00

No es necesario adjuntar elsedcomando al final del proceso aquí, ya que el separador que usamos es un carácter imprimible. De todos modos, el resultado es el mismo: en una fila determinada, cada campo se separa de su vecino mediante un delimitador de un carácter.

3. Transponer datos usando el modo serie

Los ejemplos anteriores tienen una cosa en común: elpasteEl comando lee todos sus archivos de entrada en paralelo, algo que es necesario para poder fusionarlos línea por línea en la salida.

Pero elpasteEl comando también puede operar en el llamado modo serie, habilitado mediante el-sbandera. Como su nombre lo indica, en el modo serie, elpasteEl comando leerá los archivos de entrada uno tras otro. El contenido del primer archivo de entrada se utilizará para producir la primera línea de salida. Luego, el contenido del segundo archivo de entrada se utilizará para producir la segunda línea de salida, y así sucesivamente. Eso también significa que la salida tendrá tantas líneas como archivos haya en la entrada.

Más formalmente, los datos tomados del archivo N aparecerán como la enésima línea en la salida en modo serie, mientras que aparecerían como la enésima columna en el modo "paralelo" predeterminado. En términos matemáticos, la tabla obtenida en modo serial es la transposición de la tabla producida en el modo predeterminado (y viceversa).

Para ilustrar esto, consideremos una pequeña submuestra de nuestros datos:

sh$ head -5 ACCOUNTLIB.csv | tee ACCOUNTLIB.sample
ACCOUNTLIB
TIDE SCHEDULE
VAT BS/ENC
PAYABLES
ACCOMMODATION GUIDE
sh$ head -5 ACCOUNTNUM.csv | tee ACCOUNTNUM.sample
ACCOUNTNUM
623477
445452
4356
623372

En el modo predeterminado (“paralelo”), los datos del archivo de entrada servirán como columnas en la salida, produciendo una tabla de dos columnas por cinco filas:

sh$ paste *.sample
ACCOUNTLIB    ACCOUNTNUM
TIDE SCHEDULE    623477
VAT BS/ENC    445452
PAYABLES    4356
ACCOMMODATION GUIDE    623372

Pero en el modo serie, los datos del archivo de entrada aparecerán como filas, lo que ahora produce una tabla de cinco columnas por dos filas:

sh$ paste -s *.sample
ACCOUNTLIB    TIDE SCHEDULE    VAT BS/ENC    PAYABLES    ACCOMMODATION GUIDE
ACCOUNTNUM    623477    445452    4356    623372

4. Trabajar con la entrada estándar

Como muchas utilidades estándar, lapasteEl comando puede usar la entrada estándar para leer datos. Ya sea implícitamente cuando no hay ningún nombre de archivo dado como argumento, o explícitamente usando el comando especial-Nombre del archivo. Aparentemente, esto no es tan útil:

# Here, the paste command is useless
head -5 ACCOUNTLIB.csv | paste
ACCOUNTLIB
TIDE SCHEDULE
VAT BS/ENC
PAYABLES
ACCOMMODATION GUIDE

Le recomiendo que lo pruebe usted mismo, pero la siguiente sintaxis debería producir el mismo resultado, haciendo que el comando pegar vuelva a ser inútil en ese caso:

head -5 ACCOUNTLIB.csv | paste -

Entonces, ¿cuál podría ser el sentido de leer datos de la entrada estándar? Bueno, con el-sbandera, las cosas se vuelven mucho más interesantes como lo veremos ahora.

4.1. Unir líneas de un archivo

Como lo hemos visto un par de párrafos antes, en el modo serie el comando pegar escribirá todas las líneas de un archivo de entrada en la misma línea de salida. Esto nos brinda una forma sencilla de unir todas las líneas leídas desde la entrada estándar en una sola línea de salida (potencialmente muy larga):

sh$ head -5 ACCOUNTLIB.csv | paste -s -d':'
ACCOUNTLIB:TIDE SCHEDULE:VAT BS/ENC:PAYABLES:ACCOMMODATION GUIDE

Esto es prácticamente lo mismo que podrías hacer usando eltrcomando, pero con una diferencia. usemos eldiffutilidad para detectar eso:

sh$ diff <(head -5 ACCOUNTLIB.csv | paste -s -d':') 
         <(head -5 ACCOUNTLIB.csv | tr 'n' ':')
1c1
< ACCOUNTLIB:TIDE SCHEDULE:VAT BS/ENC:PAYABLES:ACCOMMODATION GUIDE
---
> ACCOUNTLIB:TIDE SCHEDULE:VAT BS/ENC:PAYABLES:ACCOMMODATION GUIDE:
 No newline at end of file

Según lo informado por eldiffutilidad, podemos ver que el comando tr ha reemplazado cada instancia del carácter de nueva línea por el delimitador dado, incluido el último. Por otra parte, elpasteEl comando mantuvo intacto el último carácter de nueva línea. Entonces, dependiendo de si necesita el delimitador después del último campo o no, usará un comando u otro.

4.2. Formato de varias columnas de un archivo de entrada

Según las especificaciones de Open Group, “la entrada estándar se leerá una línea a la vez” por elpastedominio. Entonces, pasando varias ocurrencias del-nombre de archivo especial como argumentos para elpasteEl comando dará como resultado que tantas líneas consecutivas de la entrada se escriban en la misma línea de salida:

sh$ seq 9 | paste - - -
1    2    3
4    5    6
7    8    9

Para aclarar las cosas, te animo a que estudies la diferencia entre los dos comandos siguientes. En el primer caso, el comando pegar abre tres veces el mismo archivo, lo que genera una duplicación de datos en la salida. Por otro lado, en el segundo caso el archivo ACCOUNTLIB se abre sólo una vez (por el shell), pero se lee tres veces por cada línea (por elpastecomando), lo que da como resultado que el contenido del archivo se muestre en tres columnas:

sh$ paste ACCOUNTLIB.csv ACCOUNTLIB.csv ACCOUNTLIB.csv | head -2
ACCOUNTLIB    ACCOUNTLIB    ACCOUNTLIB
TIDE SCHEDULE    TIDE SCHEDULE    TIDE SCHEDULE

sh$ paste - - - < ACCOUNTLIB.csv | head -2
ACCOUNTLIB    TIDE SCHEDULE    VAT BS/ENC
PAYABLES    ACCOMMODATION GUIDE    VAT BS/ENC

Dado el comportamiento delpastecomando al leer desde la entrada estándar, normalmente no es aconsejable utilizar varios-nombres de archivos especiales en modo serie. En ese caso, la primera aparición leería la entrada estándar hasta su final, y las apariciones posteriores de-leería de un flujo de entrada ya agotado, lo que daría como resultado que no haya más datos disponibles:

# The following command will produce 3 lines of output.
# But the first one exhausted the standard input,
# so the remaining two lines are empty
sh$ seq 9 | paste -s - - -
1    2    3    4    5    6    7    8    9

5. Trabajar con archivos de diferente longitud

Las especificaciones de Open Group para elpasteutilidad son bastante claros:

Si se detecta una condición de fin de archivo en uno o más archivos de entrada, pero no en todos los archivos de entrada, el pegado se comportará como si se leyeran líneas vacías de los archivos en los que se detectó el fin de archivo, a menos que se utilice la opción -s. está especificado.

Entonces, el comportamiento es el que se puede esperar: los datos faltantes se reemplazan por contenido "vacío". Para ilustrar ese comportamiento, registremos un par de transacciones más en nuestra "base de datos". Sin embargo, para mantener intactos los archivos originales, trabajaremos en una copia de nuestros datos:

# Copy files
sh$ for f in ACCOUNTNUM ACCOUNTLIB CREDIT DEBIT; do
  cp ${f}.csv NEW${f}.csv
done

# Update the copy
sh$ cat - << EOF >> NEWACCOUNTNUM.csv
1080
4356
EOF

sh$ cat - << EOF >> NEWDEBIT.csv
00000001207,35

EOF

sh$ cat - << EOF >> NEWCREDIT.csv

00000001207,35
EOF

Con esas actualizaciones, ahora hemos registrado un nuevo movimiento de capital de la cuenta #1080 a la cuenta #4356. Sin embargo, como habrás notado, no me molesté en actualizar el archivo ACCOUNTLIB. Esto no parece un problema tan importante porque elpasteEl comando reemplazará las filas que faltan con datos vacíos:

sh$ paste -d';' NEWACCOUNTNUM.csv 
                NEWACCOUNTLIB.csv 
                NEWDEBIT.csv 
                NEWCREDIT.csv | tail
4356;PAYABLES;;00000000402,03
613866;RENTAL COSTS;00000000018,00;
4356;PAYABLES;;00000000018,00
657991;MISCELLANEOUS CHARGES;00000000015,00;
445333;VAT BS/DEBIT;00000000003,00;
4356;PAYABLES;;00000000018,00
626510;LANDLINE TELEPHONE;00000000069,14;
445452;VAT BS/ENC;00000000013,83;
1080;;00000001207,35; # <-- the account label is missing here
4356;;;00000001207,35 # <-- the account label is missing here

Pero ojo, elpasteEl comando solo puede hacer coincidir líneas por su posición física: todo lo que puede decir es que un archivo es "más corto" que otro. No donde faltan datos. Por lo tanto, siempre agrega los campos en blanco al final de la salida, algo que puede causar compensaciones inesperadas en sus datos. Hagámoslo obvio agregando otra transacción más:

sh$ cat << EOF >> NEWACCOUNTNUM.csv
4356
3465
EOF

sh$ cat << EOF >> NEWACCOUNTLIB.csv
PAYABLES
WEB HOSTING
EOF

sh$ cat << EOF >> NEWDEBIT.csv

00000000706,48
EOF

sh$ cat << EOF >> NEWCREDIT.csv
00000000706,48

EOF

Esta vez fui más riguroso ya que actualicé correctamente tanto el número de cuenta (ACCOUNTNUM), como su etiqueta correspondiente (ACCOUNTLIB), así como los archivos de datos de CRÉDITO y DÉBITO. Pero como faltaban datos en el registro anterior, elpasteEl comando ya no puede mantener los campos relacionados en la misma línea:

sh$ paste -d';' NEWACCOUNTNUM.csv 
                NEWACCOUNTLIB.csv 
                NEWDEBIT.csv 
                NEWCREDIT.csv | tail
4356;PAYABLES;;00000000018,00
657991;MISCELLANEOUS CHARGES;00000000015,00;
445333;VAT BS/DEBIT;00000000003,00;
4356;PAYABLES;;00000000018,00
626510;LANDLINE TELEPHONE;00000000069,14;
445452;VAT BS/ENC;00000000013,83;
1080;PAYABLES;00000001207,35;
4356;WEB HOSTING;;00000001207,35
4356;;;00000000706,48
3465;;00000000706,48;

Como puedes ver, la cuenta #4356 se reporta con la etiqueta “WEB HOSTING” mientras que, en realidad, esta última debería aparecer en la fila correspondiente a la cuenta #3465.

En conclusión, si tienes que lidiar con datos faltantes, en lugar depastecomando deberías considerar usar eljoinutilidad ya que este último hará coincidir las filas según su contenido y no según su posición en el archivo de entrada. Eso lo hace mucho más adecuado para aplicaciones de estilo “base de datos”. Ya publiqué un vídeo sobre eljoincomando, pero probablemente debería merecer un artículo propio, ¡así que háganos saber si está interesado en ese tema!

6. Pasar por encima de los delimitadores

En la inmensa mayoría de los casos de uso, proporcionará solo un carácter como delimitador. Esto es lo que hemos hecho hasta ahora. Sin embargo, si das varios caracteres después del-dopción, el comando pegar pasará por ellos: el primer carácter se utilizará como primer delimitador de campo en la fila, el segundo carácter como segundo delimitador de campo, y así sucesivamente.

sh$ paste -d':+-' ACCOUNT*.csv CREDIT.csv DEBIT.csv | head -5
ACCOUNTLIB:ACCOUNT NUM+CREDIT-DEBIT
TIDE SCHEDULE:623477+-00000001615,00
VAT BS/ENC:445452+-00000000323,00
PAYABLES:4356+00000001938,00-
ACCOMODATION GUIDE:623372+-00000001333,00

Los delimitadores de campos solo pueden aparecer entre campos. No al final de una línea. Y no puede insertar más de un delimitador entre dos campos determinados. Como truco para superar estas limitaciones, puedes utilizar el/dev/nullarchivo especial como entrada adicional donde necesita un separador adicional:

# Display the opening bracket between the
# ACCOUNTLIB field and the ACCOUNTNUM field, and
# the closing bracket between the ACCOUNTNUM field
# and the empty `/dev/null` field:
sh$ paste  -d'()' 
           ACCOUNT*.csv /dev/null | head -5
ACCOUNTLIB(ACCOUNTNUM)
TIDE SCHEDULE(623477)
VAT BS/ENC(445452)
PAYABLES(4356)
ACCOMODATION GUIDE(623372)

Algo de lo que incluso puedes abusar:

sh$ paste -d'# is ' 
          - ACCOUNTNUM.csv - - - ACCOUNTLIB.csv < /dev/null | tail -5
#657991 is MISCELLANEOUS CHARGES
#445333 is VAT BS/DEBIT
#4356 is PAYABLES
#626510 is LANDLINE TELEPHONE
#445452 is VAT BS/ENC

Sin embargo, no hace falta decir que si alcanzas ese nivel de complejidad, podría ser una pista de quepasteLa utilidad no era necesariamente la mejor herramienta para el trabajo. Quizás valga la pena considerar, en ese caso, algo más comosedo comando awk.

Pero, ¿qué pasa si la lista contiene menos delimitadores de los necesarios para mostrar una fila en la salida? Curiosamente, elpasteEl comando “ciclo” sobre ellos. Entonces, una vez agotada la lista, elpasteEl comando volverá al primer delimitador, algo que probablemente abra la puerta a algún uso creativo. Por mi parte, no pude hacer nada realmente útil con esa función dados mis datos. Así que tendrás que conformarte con el siguiente ejemplo, un poco descabellado. Pero no será una completa pérdida de tiempo ya que fue una buena ocasión para mencionar que debes duplicar la barra invertida (\) cuando quieras usarlo como delimitador:

sh$ paste -d'/\' 
          - ACCOUNT*.csv CREDIT.csv DEBIT.csv - < /dev/null | tail -5
/MISCELLANEOUS CHARGES657991/�0000000015,00/
/VAT BS/DEBIT445333/�0000000003,00/
/PAYABLES4356/00000000018,00/
/LANDLINE TELEPHONE626510/�0000000069,14/
/VAT BS/ENC445452/�0000000013,83/

7. Delimitadores de caracteres multibyte

Como la mayoría de las utilidades estándar de Unix, el comando pegar nace en un momento en que un carácter equivalía a un byte. Pero este ya no es el caso: hoy en día, muchos sistemas utilizan la codificación de longitud variable UTF-8 de forma predeterminada. En UTF-8, un carácter puede representarse con 1, 2, 3 o 4 bytes. Eso nos permite mezclar en el mismo archivo de texto toda la variedad de escritura humana, así como toneladas de símbolos y emojis, manteniendo al mismo tiempo la compatibilidad ascendente con la codificación de caracteres US-ASCII heredada de un byte.

Digamos, por ejemplo, que me gustaría usar el DIAMANTE BLANCO (◇ U+25C7) como mi separador de campo. En UTF-8, este carácter se codifica utilizando los tres bytese2 97 87. Este carácter puede ser difícil de obtener desde el teclado, por lo que si quieres probarlo tú mismo, te sugiero que lo copie y pegue desde el bloque de código siguiente:

# The sed part is only used as a little trick to add the
# row number as the first field in the output
sh$ sed -n = ACCOUNTNUM.csv |
       paste -d'◇' - ACCOUNT*.csv | tail -5
26�MISCELLANEOUS CHARGES�657991
27�VAT BS/DEBIT�445333
28�PAYABLES�4356
29�LANDLINE TELEPHONE�626510
30�VAT BS/ENC�445452

Bastante engañoso, ¿no? En lugar del esperado diamante blanco, tengo ese símbolo de "signo de interrogación" (al menos, así es como se muestra en mi sistema). Sin embargo, no es un personaje "aleatorio". Es el carácter de reemplazo de Unicode que se utiliza "para indicar problemas cuando un sistema no puede representar un flujo de datos en un símbolo correcto". ¿Entonces qué ha ido mal?

Una vez más, examinar el contenido binario sin procesar de la salida nos dará algunas pistas:

sh$ sed -n = ACCOUNTNUM.csv | paste -d'◇' - ACCOUNT*.csv | tail -5 | hexdump -C
00000000  32 36 e2 4d 49 53 43 45  4c 4c 41 4e 45 4f 55 53  |26.MISCELLANEOUS|
00000010  20 43 48 41 52 47 45 53  97 36 35 37 39 39 31 0a  | CHARGES.657991.|
00000020  32 37 e2 56 41 54 20 42  53 2f 44 45 42 49 54 97  |27.VAT BS/DEBIT.|
00000030  34 34 35 33 33 33 0a 32  38 e2 50 41 59 41 42 4c  |445333.28.PAYABL|
00000040  45 53 97 34 33 35 36 0a  32 39 e2 4c 41 4e 44 4c  |ES.4356.29.LANDL|
00000050  49 4e 45 20 54 45 4c 45  50 48 4f 4e 45 97 36 32  |INE TELEPHONE.62|
00000060  36 35 31 30 0a 33 30 e2  56 41 54 20 42 53 2f 45  |6510.30.VAT BS/E|
00000070  4e 43 97 34 34 35 34 35  32 0a                    |NC.445452.|
0000007a

Ya tuvimos la oportunidad de practicar con volcados hexadecimales arriba, por lo que ahora sus ojos deberían estar lo suficientemente atentos para detectar los delimitadores de campo en el flujo de bytes. Si miras de cerca, verás que el separador de campo después del número de línea es el byte.e2. Pero si continúa sus investigaciones, notará que el segundo separador de campo es97. No sólo elpaste¿¡¿El comando no generó el carácter que quería, pero tampoco usó en todas partes el mismo byte como separador?!?

Espera un momento: ¿eso no te recuerda algo de lo que ya hablamos? Y esos dos bytese2 97¿No te resultan algo familiares? Bueno, familiar probablemente sea demasiado, pero si retrocede algunos párrafos, es posible que los encuentres mencionados en alguna parte...

Entonces, ¿encontraste dónde estaba? Anteriormente, dije en UTF-8, el diamante blanco está codificado como los tres bytese2 97 87. Y efectivamente, elpasteEl comando ha considerado esa secuencia no como un carácter completo de tres bytes, sino como tres bytes independientes, por lo que utilizó el primer byte como primer separador de campo y luego el segundo byte como segundo separador de campo.

Te dejo volver a ejecutar ese experimento agregando una columna más en los datos de entrada; Deberías ver que el tercer separador de campo es87— el tercer byte de la representación UTF-8 del diamante blanco.

Ok, esa es la explicación: elpasteEl comando solo acepta “caracteres” de un byte como separador. Y eso es particularmente molesto, ya que, una vez más, no conozco ninguna forma de superar esa limitación excepto usando el/dev/nulltruco que ya te di:

sh$ sed -n = ACCOUNTNUM.csv |
    paste  -d'◇' 
           - /dev/null /dev/null 
           ACCOUNTLIB.csv /dev/null /dev/null 
           ACCOUNTNUM.csv | tail -5
26◇MISCELLANEOUS CHARGES◇657991
27◇VAT BS/DEBIT◇445333
28◇PAYABLES◇4356
29◇LANDLINE TELEPHONE◇626510
30◇VAT BS/ENC◇445452

Si lees mi artículo anterior sobre elcutcomando, tal vez recuerdes que tuve problemas similares con la implementación GNU de esa herramienta. Pero noté en ese momento que la implementación de OpenBSD estaba tomando en cuenta correctamenteLC_CTYPEconfiguración regional para identificar caracteres multibyte. Por curiosidad, he probado elpastecomando en OpenBSD también. Desgraciadamente, esta vez con el mismo resultado que en mi Debian, a pesar de las especificaciones delpasteutilidad que menciona la variable de entorno LC_CTYPE como determinante de "la configuración regional para la interpretación de secuencias de bytes de datos de texto como caracteres (por ejemplo, caracteres de un solo byte en lugar de caracteres de varios bytes en argumentos y archivos de entrada)". Desde mi experiencia, todas las implementaciones principales delpasteLa utilidad actualmente ignora los caracteres de varios bytes en la lista de delimitadores y asume separadores de un byte. Pero no afirmaré haberlo probado para toda la variedad de plataformas *nix. Entonces, si me perdí algo aquí, ¡no dudes en usar la sección de comentarios para corregirme!

Consejo adicional: evitar el peligro

Por razones históricas:

Los comandos:
pegar -d “”… pegar -d “”…
no son necesariamente equivalentes; este último no está especificado en este volumen de IEEE Std 1003.1-2001 y puede provocar un error. La construcción ' ' se utiliza para significar "sin separador" porque las versiones históricas de pegar no siguieron las pautas de sintaxis y el comando:
pegar -d””…
getopt() no pudo manejarlo correctamente.

Entonces, la forma portátil de pegar archivos sin usar un delimitador es especificando eldelimitador. Esto es algo contradictorio ya que, para muchos comandos,significa el carácter NUL: un carácter codificado como un byte compuesto únicamente de ceros que no debe chocar con ningún contenido de texto.

Es posible que el carácter NUL le resulte útil como separador, especialmente cuando sus datos pueden contener caracteres arbitrarios (como cuando trabaja con nombres de archivos o datos proporcionados por el usuario). Desafortunadamente, no conozco ninguna forma de utilizar el carácter NUL como delimitador de campo con elpastedominio. ¿Pero tal vez sabes cómo hacer eso? Si ese es el caso, estaría más que feliz de leer su solución en la sección de comandos.

Por otra parte, elpasteparte de implementación de GNU Coreutils tiene la característica no estándar-zopción para cambiar del carácter de nueva línea al carácter NUL para el separador de línea. Pero en ese caso, el carácter NUL se utilizará como separador de línea tanto para la entrada como para la salida. Entonces, para probar esa característica, primero necesitamos una versión terminada en cero de nuestros archivos de entrada:

sh$ tr 'n' '�' < ACCOUNTLIB.csv > ACCOUNTLIB.zero
sh$ tr 'n' '�' < ACCOUNTNUM.csv > ACCOUNTNUM.zero

Para ver qué ha cambiado en el proceso, podemos utilizar elhexdumputilidad para examinar el contenido binario sin formato de los archivos:

sh$ hexdump -C ACCOUNTLIB.csv | head -5
00000000  41 43 43 4f 55 4e 54 4c  49 42 0a 54 49 44 45 20  |ACCOUNTLIB.TIDE |
00000010  53 43 48 45 44 55 4c 45  0a 56 41 54 20 42 53 2f  |SCHEDULE.VAT BS/|
00000020  45 4e 43 0a 50 41 59 41  42 4c 45 53 0a 41 43 43  |ENC.PAYABLES.ACC|
00000030  4f 4d 4f 44 41 54 49 4f  4e 20 47 55 49 44 45 0a  |OMODATION GUIDE.|
00000040  56 41 54 20 42 53 2f 45  4e 43 0a 50 41 59 41 42  |VAT BS/ENC.PAYAB|
sh$ hexdump -C ACCOUNTLIB.zero | head -5
00000000  41 43 43 4f 55 4e 54 4c  49 42 00 54 49 44 45 20  |ACCOUNTLIB.TIDE |
00000010  53 43 48 45 44 55 4c 45  00 56 41 54 20 42 53 2f  |SCHEDULE.VAT BS/|
00000020  45 4e 43 00 50 41 59 41  42 4c 45 53 00 41 43 43  |ENC.PAYABLES.ACC|
00000030  4f 4d 4f 44 41 54 49 4f  4e 20 47 55 49 44 45 00  |OMODATION GUIDE.|
00000040  56 41 54 20 42 53 2f 45  4e 43 00 50 41 59 41 42  |VAT BS/ENC.PAYAB|

Le dejaré comparar usted mismo los dos volcados hexadecimales anteriores para identificar la diferencia entre los archivos ".zero" y los archivos de texto originales. Como pista, puedo decirle que una nueva línea está codificada como0abyte.

Con suerte, se tomó el tiempo necesario para localizar el carácter NUL en los archivos de entrada ".zero". De todos modos, ahora tenemos una versión terminada en cero de los archivos de entrada, por lo que podemos usar el-zopción de lapastecomando para manejar esos datos, produciendo en la salida también un resultado terminado en cero:

# Hint: in the hexadecimal dump:
#  the byte 00 is the NUL character
#  the byte 09 is the TAB character
# Look at any ASCII table to find the mapping
# for the letters or other symbols
# (https://en.wikipedia.org/wiki/ASCII#Character_set)
sh$ paste -z *.zero | hexdump -C | head -5
00000000  41 43 43 4f 55 4e 54 4c  49 42 09 41 43 43 4f 55  |ACCOUNTLIB.ACCOU|
00000010  4e 54 4e 55 4d 00 54 49  44 45 20 53 43 48 45 44  |NTNUM.TIDE SCHED|
00000020  55 4c 45 09 36 32 33 34  37 37 00 56 41 54 20 42  |ULE.623477.VAT B|
00000030  53 2f 45 4e 43 09 34 34  35 34 35 32 00 50 41 59  |S/ENC.445452.PAY|
00000040  41 42 4c 45 53 09 34 33  35 36 00 41 43 43 4f 4d  |ABLES.4356.ACCOM|

# Using the `tr` utility, we can map � to newline
# in order to display the output on the console:
sh$ paste -z *.zero | tr '�' 'n' | head -3
ACCOUNTLIB    ACCOUNTNUM
TIDE SCHEDULE    623477
VAT BS/ENC    445452

Dado que mis archivos de entrada no contienen nuevas líneas incrustadas en los datos, el-zLa opción es de utilidad limitada aquí. Pero basándome en las explicaciones anteriores, le dejo que intente comprender por qué el siguiente ejemplo funciona "como se esperaba". Para comprenderlo completamente, probablemente necesite descargar los archivos de muestra y examinarlos a nivel de bytes usando elhexdumputilidad como hicimos arriba:

# Somehow, the head utility seems to be confused
# by the ACCOUNTS file content (I wonder why?;)
sh$ head -3 CATEGORIES ACCOUNTS
==> CATEGORIES <==
PRIVATE
ACCOMMODATION GUIDE
SHARED

==> ACCOUNTS <==
6233726230846265106159126579914356613866618193623477623795445333445452605751
# The output is quite satisfactory, putting the account number
# after the account name and keeping things surprisingly nicely formatted:
sh$ paste -z -d':' CATEGORIES ACCOUNTS | tr '�' 'n' | head -5
PRIVATE
ACCOMMODATION GUIDE:623372

SHARED
S:623084

¿Y lo que es más?

ElpasteEl comando produce sólo salida de texto delimitado. Pero como se ilustra al final del vídeo introductorio, si su sistema admite BSDcolumnutilidad, puede utilizarla para obtener tablas con un buen formato convirtiendo elpastesalida del comando a un formato de texto de ancho fijo. Pero ese será el tema de un próximo artículo. Así que estad atentos y, como siempre, ¡no olvides compartir ese artículo en tus sitios web y redes sociales favoritos!

Actualizaciones del boletín

Ingrese su dirección de correo electrónico a continuación para suscribirse a nuestro boletín