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.
Elpaste
El 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, elpaste
El 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: elpaste
El 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, elpaste
El comando utiliza el carácter de tabulación como separador de campo predeterminado (“delimitador”). Algo que podemos cambiar usando el-d
opció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 elsed
comando 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: elpaste
El 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 elpaste
El comando también puede operar en el llamado modo serie, habilitado mediante el-s
bandera. Como su nombre lo indica, en el modo serie, elpaste
El 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, lapaste
El 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-s
bandera, 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 eltr
comando, pero con una diferencia. usemos eldiff
utilidad 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 eldiff
utilidad, 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, elpaste
El 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 elpaste
dominio. Entonces, pasando varias ocurrencias del-
nombre de archivo especial como argumentos para elpaste
El 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 elpaste
comando), 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 delpaste
comando 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 elpaste
utilidad 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 elpaste
El 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, elpaste
El 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, elpaste
El 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 depaste
comando deberías considerar usar eljoin
utilidad 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 eljoin
comando, 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-d
opció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/null
archivo 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 quepaste
La utilidad no era necesariamente la mejor herramienta para el trabajo. Quizás valga la pena considerar, en ese caso, algo más comosed
o comando awk.
Pero, ¿qué pasa si la lista contiene menos delimitadores de los necesarios para mostrar una fila en la salida? Curiosamente, elpaste
El comando “ciclo” sobre ellos. Entonces, una vez agotada la lista, elpaste
El 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, elpaste
El 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: elpaste
El 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/null
truco 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 elcut
comando, 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_CTYPE
configuración regional para identificar caracteres multibyte. Por curiosidad, he probado elpaste
comando en OpenBSD también. Desgraciadamente, esta vez con el mismo resultado que en mi Debian, a pesar de las especificaciones delpaste
utilidad 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 delpaste
La 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 elpaste
dominio. ¿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, elpaste
parte de implementación de GNU Coreutils tiene la característica no estándar-z
opció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 elhexdump
utilidad 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 como0a
byte.
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-z
opción de lapaste
comando 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-z
La 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 elhexdump
utilidad 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?
Elpaste
El comando produce sólo salida de texto delimitado. Pero como se ilustra al final del vídeo introductorio, si su sistema admite BSDcolumn
utilidad, puede utilizarla para obtener tablas con un buen formato convirtiendo elpaste
salida 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!