GNU/Linux >> Tutoriales Linux >  >> Linux

7 Uso esencial y práctico del comando Pegar en Linux

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

El paste 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 usar efectivamente el comando Pegar en Linux y Unix.

7 ejemplos prácticos del comando Pegar en Linux

Si prefiere videos, puede ver este video que explica los mismos ejemplos del comando Pegar discutidos en este artículo.

1. Pegar columnas

En su caso de uso más básico, paste el comando toma N archivos de entrada y unirlos línea por línea en la salida:

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

sh$ printf "%s\n" {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 video anterior, puede 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 muy fácil producir un archivo de texto delimitado por tabuladores 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 tabuladores no produce una tabla perfectamente formateada. Pero esto es por diseño:el paste El comando no se usa 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 campo.

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

sh$ paste *.csv | head -3 | sed -n l
ACCOUNTLIB\tACCOUNTNUM\tCREDIT\tDEBIT$
TIDE SCHEDULE\t623477\t\t00000001615,00$
VAT BS/ENC\t445452\t\t00000000323,00$

Ahora, los caracteres invisibles se muestran sin ambigüedades en la salida. Y puede ver los caracteres de tabulación que se muestran como \t . Puede contarlos:siempre hay tres pestañas en cada línea de salida, una entre cada campo. Y cuando ves dos de ellos en una fila, eso solo 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, el paste El comando utiliza el carácter de tabulación como separador de campo predeterminado ("delimitador"). Algo que podemos cambiar usando -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 agregar el sed comando al final de la tubería 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. Transposición de datos usando el modo serial

Los ejemplos anteriores tienen una cosa en común:el paste El comando lee todos sus archivos de entrada en paralelo, algo que se requiere para que pueda fusionarlos línea por línea en la salida.

Pero el paste El comando también puede operar en el llamado modo serie , habilitado usando -s bandera. Como su nombre lo indica, en el modo serial, el paste 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 usará 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á como N línea en la salida en modo serie, mientras que aparecería como N ª columna en el modo “paralelo” por defecto. En términos matemáticos, la tabla obtenida en modo serie es la transposición de la tabla producida en el modo por defecto (y viceversa ).

Para ilustrar eso, 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 modo serie, los datos del archivo de entrada aparecerán como filas, produciendo ahora 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. Trabajando con la entrada estándar

Como muchas utilidades estándar, paste El comando puede usar la entrada estándar para leer datos. Ya sea implícitamente cuando no se proporciona un nombre de archivo como argumento, o explícitamente usando el - 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

Te animo a que lo pruebes por ti mismo, pero la siguiente sintaxis debería producir el mismo resultado, haciendo que el comando pegar sea inútil una vez más en ese caso:

head -5 ACCOUNTLIB.csv | paste -

Entonces, ¿cuál podría ser el punto 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 manera simple de unir todas las líneas leídas de 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 básicamente lo mismo que podrías hacer usando tr comando, pero con una diferencia sin embargo. Usemos el diff 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 el diff utilidad, podemos ver el tr El comando ha reemplazado cada instancia del carácter de nueva línea por el delimitador dado, incluido el último. Por otro lado, el paste 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

De acuerdo con las especificaciones de Open Group, “la entrada estándar debe leerse una línea a la vez” por el paste dominio. Entonces, pasando varias apariciones de - nombre de archivo especial como argumentos para paste El comando dará como resultado que se escriban tantas líneas consecutivas de la entrada en la misma línea de salida:

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

Para aclarar las cosas, lo animo a estudiar la diferencia entre los dos comandos a continuación. 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 solo una vez (por el shell), pero se lee tres veces por cada línea (por el paste comando), lo que hace 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 del paste comando cuando se lee desde la entrada estándar, por lo general es no recomendable utilizar varios - nombres de archivos especiales en modo serie. En ese caso, la primera aparición leería la entrada estándar hasta el final, y las siguientes apariciones 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. Trabajando con archivos de diferente longitud

Las especificaciones de Open Group para paste utilidad son bastante claras:

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, pegar 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 el Se especifica la opción -s.

Por lo tanto, el comportamiento es el esperado:los datos que faltan 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 grande porque paste 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, el paste El comando solo puede hacer coincidir líneas por su físico posición:todo lo que puede decir es que un archivo es "más corto" que otro. No dónde faltan los 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:

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 DEBITO. Pero como faltaban datos en el registro anterior, paste 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 puede ver, la cuenta #4356 se reporta con la etiqueta “WEB HOSTING” cuando, en realidad, este último debería aparecer en la fila correspondiente a la cuenta #3465.

Como conclusión, si tiene que lidiar con datos faltantes, en lugar de paste comando debería considerar usar join utilidad ya que este último coincidirá con las filas en función de su contenido, y no en función de 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 video sobre el join comando, pero eso probablemente debería merecer un artículo propio, ¡así que háganos saber si está interesado en ese tema!

6. Ciclismo sobre delimitadores

En la gran mayoría de los casos de uso, solo proporcionará un carácter como delimitador. Esto es lo que hemos hecho hasta ahora. Sin embargo, si da varios caracteres después de -d opción, el comando de pegado se desplazará sobre ellos:el primer carácter se utilizará como el primer delimitador de campo en la fila, el segundo carácter como el 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 campo solo pueden aparecer entre los campos. No al final de una línea. Y no puede insertar más de un delimitador entre dos campos dados. Como truco para superar estas limitaciones, puede usar 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 es necesario decirlo, si alcanza ese nivel de complejidad, podría ser una pista el paste la utilidad no era necesariamente la mejor herramienta para el trabajo. Tal vez valga la pena considerar, en ese caso, algo más como sed o comando awk.

Pero, ¿qué sucede si la lista contiene menos delimitadores de los necesarios para mostrar una fila en la salida? Curiosamente, el paste el comando "ciclo" sobre ellos. Entonces, una vez que se agote la lista, paste 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 debe duplicar la barra invertida (\\ ) cuando quiera usarlo como delimitador:

sh$ paste -d'/\\' \
          - ACCOUNT*.csv CREDIT.csv DEBIT.csv - < /dev/null | tail -5
/MISCELLANEOUS CHARGES\657991/\00000000015,00/
/VAT BS/DEBIT\445333/\00000000003,00/
/PAYABLES\4356/00000000018,00\/
/LANDLINE TELEPHONE\626510/\00000000069,14/
/VAT BS/ENC\445452/\00000000013,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 estar representado por 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, mientras mantenemos una 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 bytes e2 97 87 . Este carácter puede ser difícil de obtener desde el teclado, por lo que si desea probarlo usted mismo, le sugiero que lo copie y pegue desde el bloque de código a continuación:

# 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 diamante blanco esperado, 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 utilizado "para indicar problemas cuando un sistema no puede representar un flujo de datos en un símbolo correcto" . Entonces, ¿qué salió 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 sus ojos ahora deberían estar lo suficientemente alertas para detectar los delimitadores de campo en el flujo de bytes. Al mirar de cerca, verá que el separador de campo después del número de línea es el byte e2 . Pero si continúa con sus investigaciones, notará que el segundo separador de campo es 97 . No solo el paste El comando no generó el carácter que quería, pero tampoco usó en todas partes el mismo byte que el separador?!?

Espera un minuto:¿no te recuerda eso algo de lo que ya hablamos? Y esos dos bytes e2 97 , ¿no te resultan algo familiares? Bueno, familiar es probablemente un poco demasiado, pero si retrocedes unos pocos párrafos, es posible que los encuentres mencionados en alguna parte...

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

Le permití volver a ejecutar ese experimento agregando una columna más en los datos de entrada; deberías ver que el tercer separador de campo es 87 — el tercer byte de la representación UTF-8 para el diamante blanco.

Ok, esa es la explicación:el paste 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 el cut comando, puede recordar que tuve problemas similares con la implementación GNU de esa herramienta. Pero noté en ese momento que la implementación de OpenBSD estaba teniendo en cuenta correctamente el LC_CTYPE configuración regional para identificar caracteres de varios bytes. Por curiosidad, he probado el paste comando en OpenBSD también. Por desgracia, esta vez con el mismo resultado que en mi caja de Debian, a pesar de las especificaciones para paste 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)” . Según mi experiencia, todas las principales implementaciones de paste Actualmente, la utilidad ignora los caracteres de varios bytes en la lista de delimitadores y supone separadores de un byte. Pero no diré que lo he 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 extra:Evitar la trampa \0

Por razones históricas:

Los comandos:
pegar -d “\0” …​ pegar -d “” …​
no son necesariamente equivalentes; este último no se especifica en este volumen de IEEE Std 1003.1-2001 y puede generar un error. La construcción '\0' se usa 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 \0 delimitador Esto es algo contradictorio ya que, para muchos comandos, \0 significa el carácter NUL, un carácter codificado como un byte hecho solo de ceros que no debe entrar en conflicto con ningún contenido de texto.

Puede encontrar el carácter NUL como un separador útil, 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 usar el carácter NUL como delimitador de campo con paste dominio. Pero tal vez usted sabe 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 otro lado, el paste la parte de implementación de GNU Coreutils tiene el -z no estándar opción para cambiar de la nueva línea al carácter NUL para el separador de línea. Pero en ese caso, el carácter NUL se usará como separador de línea ambos para la entrada y la salida. Entonces, para probar esa función, primero necesitamos una versión terminada en cero de nuestros archivos de entrada:

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

Para ver qué ha cambiado en el proceso, podemos usar hexdump utilidad para examinar el contenido binario sin procesar 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 por sí mismo los dos volcados hexadecimales anteriores para identificar la diferencia entre los archivos ".zero" y los archivos de texto originales. Como pista, puedo decirte que una nueva línea está codificada como 0a byte.

Con suerte, se tomó el tiempo necesario para ubicar 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 -z opción del paste 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 \0 to newline
# in order to display the output on the console:
sh$ paste -z *.zero | tr '\0' '\n' | head -3
ACCOUNTLIB    ACCOUNTNUM
TIDE SCHEDULE    623477
VAT BS/ENC    445452

Dado que mis archivos de entrada no contienen saltos de línea incrustados en los datos, -z opción es de utilidad limitada aquí. Pero basándome en las explicaciones anteriores, te dejo que intentes entender por qué el siguiente ejemplo funciona "como se esperaba". Para comprender completamente que probablemente necesite descargar los archivos de muestra y examinarlos a nivel de byte usando el hexdump utilidad como lo hicimos anteriormente:

# 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 '\0' '\n' | head -5
PRIVATE
ACCOMMODATION GUIDE:623372

SHARED
ADVERTISEMENTS:623084

¿Qué más?

El paste El comando produce solo una salida de texto delimitado. Pero como se ilustra al final del video introductorio, si su sistema admite la column de BSD utilidad, puede usarla para obtener tablas bien formateadas convirtiendo el paste salida de 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 olvidéis compartir ese artículo en vuestros sitios web y redes sociales favoritos!


Linux
  1. Comando Cat de Linux:uso y ejemplos

  2. El comando Sed de Linux:uso y ejemplos

  3. Comandos de Linux:trabajos, bg y fg

  4. El comando AWK de Linux:ejemplos de sintaxis de uso de Linux y Unix

  5. Comando df de Linux

50 comandos grep productivos y prácticos para entusiastas de Linux

16 Ejemplos Prácticos y Útiles del Comando Echo en Linux

15 ejemplos prácticos del comando Rsync en Linux

5 ejemplos prácticos de Tail Command en Linux

7 Uso esencial y práctico del comando Pegar en Linux

Cat Command en Linux:ejemplos esenciales y avanzados