GNU/Linux >> Linux Esercitazione >  >> Linux

7 Utilizzo essenziale e pratico del comando Incolla in Linux

In un precedente articolo abbiamo parlato del comando di taglio che può essere utilizzato per estrarre colonne da un file CSV o da un file di dati di testo tabulare.

Il paste comando fa l'esatto opposto:unisce diversi file di input per produrre da essi un nuovo file di testo delimitato. Vedremo come utilizzare efficacemente il comando Incolla in Linux e Unix.

7 esempi pratici di comando Incolla in Linux

Se preferisci i video, puoi guardare questo video che spiega gli stessi esempi di comandi Incolla discussi in questo articolo.

1. Incollare colonne

Nel suo caso d'uso più elementare, paste il comando richiede N file di input e uniscili riga per riga sull'output:

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

Ma lasciamo ora le spiegazioni teoriche a lavorare su un esempio pratico. Se hai scaricato i file di esempio utilizzati nel video sopra, puoi vedere che ho diversi file di dati corrispondenti alle varie colonne di una tabella:

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

È abbastanza facile produrre un file di testo delimitato da tabulazioni da quei dati:

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

Come puoi vedere, quando viene visualizzato sulla console, il contenuto di quel file di valori separati da tabulazioni non produce una tabella perfettamente formattata. Ma questo è di progettazione:il paste Il comando non viene utilizzato per creare file di testo a larghezza fissa, ma solo file di testo delimitati in cui a un determinato carattere viene assegnato il ruolo di separatore di campo.

Quindi, anche se non è ovvio nell'output sopra, in realtà c'è uno e solo uno carattere di tabulazione tra ogni campo. Rendiamolo evidente usando il 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$

Ora, i caratteri invisibili vengono visualizzati in modo inequivocabile nell'output. E puoi vedere i caratteri di tabulazione visualizzati come \t . Puoi contarli:ci sono sempre tre schede su ogni riga di output, una tra ogni campo. E quando ne vedi due di fila, significa solo che c'era un campo vuoto lì. Questo è spesso il caso nei miei particolari file di esempio poiché su ogni riga è impostato il campo CREDITO o DEBITO, ma mai entrambi contemporaneamente.

2. Modifica del delimitatore di campo

Come abbiamo visto, paste Il comando utilizza il carattere di tabulazione come separatore di campo predefinito ("delimitatore"). Qualcosa che possiamo cambiare usando -d opzione. Diciamo che vorrei invece usare un punto e virgola:

# 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

Non c'è bisogno di aggiungere il sed comando alla fine della pipeline qui poiché il separatore che abbiamo usato è un carattere stampabile. Ad ogni modo, il risultato è lo stesso:su una data riga, ogni campo è separato dal suo vicino usando un delimitatore di un carattere.

3. Trasporre i dati utilizzando la modalità seriale

Gli esempi sopra hanno una cosa in comune:paste comando legge tutti i suoi file di input in parallelo, cosa necessaria in modo da poterli unire riga per riga nell'output.

Ma il paste comando può operare anche nella cosiddetta modalità seriale , abilitato utilizzando il -s bandiera. Come suggerisce il nome, in modalità seriale, il paste il comando leggerà i file di input uno dopo l'altro. Il contenuto del primo file di input verrà utilizzato per produrre la prima riga di output. Quindi il contenuto del secondo file di input verrà utilizzato per produrre la seconda riga di output e così via. Ciò significa anche che l'output avrà tante righe quanti sono i file nell'input.

Più formalmente, i dati presi dal file N apparirà come N esima riga nell'output in modalità seriale, mentre apparirà come N a colonna nella modalità predefinita “parallelo”. In termini matematici, la tabella ottenuta in modalità seriale è la trasposizione della tabella prodotta in modalità di default (e viceversa ).

Per illustrarlo, consideriamo un piccolo sottocampione dei nostri dati:

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

Nella modalità predefinita ("parallela"), i dati del file di input fungeranno da colonne nell'output, producendo una tabella di due colonne per cinque righe:

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

Ma in modalità seriale, i dati del file di input appariranno come righe, producendo ora una tabella di cinque colonne per due righe:

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

4. Lavorare con l'input standard

Come molte utilità standard, paste comando può utilizzare l'input standard per leggere i dati. O implicitamente quando non c'è un nome file dato come argomento, o esplicitamente usando lo speciale - nome del file. Apparentemente, questo non è così utile però:

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

Ti incoraggio a testarlo da solo, ma la seguente sintassi dovrebbe produrre lo stesso risultato, rendendo ancora una volta il comando incolla inutile in quel caso:

head -5 ACCOUNTLIB.csv | paste -

Quindi, quale potrebbe essere il punto di leggere i dati dallo standard input? Bene, con il -s flag, le cose diventano molto più interessanti come lo vedremo ora.

4.1. Righe di unione di un file

Come abbiamo visto un paio di paragrafi prima, in modalità seriale il comando incolla scriverà tutte le righe di un file di input sulla stessa riga di output. Questo ci offre un modo semplice per unire tutte le righe lette dallo standard input in una sola riga di output (potenzialmente molto lunga):

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

Questa è più o meno la stessa cosa che potresti fare usando tr comando, ma con una differenza però. Usiamo il diff utilità per individuarlo:

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

Come riportato da diff utilità, possiamo vedere il tr comando ha sostituito ogni istanza del carattere di nuova riga con il delimitatore dato, incluso l'ultimo. D'altra parte, il paste comando ha mantenuto intatto l'ultimo carattere di nuova riga. Quindi, a seconda che tu abbia bisogno del delimitatore dopo l'ultimo campo o meno, utilizzerai un comando o l'altro.

4.2. Formattazione a più colonne di un file di input

Secondo le specifiche di Open Group, "l'input standard deve essere letto una riga alla volta" con paste comando. Quindi, passando diverse occorrenze di - nome file speciale come argomenti per paste il comando risulterà con tante righe consecutive dell'input che verranno scritte nella stessa riga di output:

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

Per rendere le cose più chiare, ti incoraggio a studiare la differenza tra i due comandi seguenti. Nel primo caso, il comando incolla apre tre volte lo stesso file, con conseguente duplicazione dei dati nell'output. Nel secondo caso, invece, il file ACCOUNTLIB viene aperto una sola volta (dalla shell), ma letto tre volte per ogni riga (tramite paste comando), con il risultato che il contenuto del file viene visualizzato come tre colonne:

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

Dato il comportamento di paste comando durante la lettura dallo standard input, di solito è non consigliabile utilizzare diversi - nomi di file speciali in modalità seriale. In tal caso, la prima occorrenza leggerà lo standard input fino alla sua fine e le successive occorrenze di - leggerebbe da un flusso di input già esaurito, con il risultato che non saranno più disponibili dati:

# 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. Lavorare con file di diversa lunghezza

Le specifiche di Open Group per paste utilità sono abbastanza chiare:

Se viene rilevata una condizione di fine file su uno o più file di input, ma non su tutti i file di input, incolla si comporterà come se fossero state lette righe vuote dai file su cui è stata rilevata la fine del file, a meno che il -s è specificata l'opzione.

Quindi, il comportamento è quello che potresti aspettarti:i dati mancanti vengono sostituiti da contenuti "vuoti". Per illustrare questo comportamento, registriamo un altro paio di transazioni nel nostro "database". Per mantenere intatti i file originali, lavoreremo su una copia dei nostri dati però:

# 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 questi aggiornamenti, abbiamo ora registrato un nuovo movimento di capitale dal conto n. 1080 al conto n. 4356. Tuttavia, come avrai notato, non mi sono preoccupato di aggiornare il file ACCOUNTLIB. Questo non sembra un grosso problema perché paste il comando sostituirà le righe mancanti con dati vuoti:

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

Ma attenzione, paste il comando può abbinare le righe solo in base al loro fisico posizione:tutto ciò che può dire è che un file è "più corto" di un altro. Non dove mancano i dati. Quindi aggiunge sempre i campi vuoti alla fine dell'output, cosa che può causare offset imprevisti nei tuoi dati. Rendiamolo ovvio aggiungendo un'altra transazione:

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

Questa volta, sono stato più rigoroso poiché ho aggiornato correttamente sia il numero di conto (ACCOUNTNUM) che l'etichetta corrispondente (ACCOUNTLIB) nonché i file di dati CREDIT e DEBIT. Ma poiché mancavano dei dati nel record precedente, paste comando non è più in grado di mantenere i relativi campi sulla stessa riga:

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;

Come puoi vedere, l'account #4356 è riportato con l'etichetta “WEB HOSTING” mentre, in realtà, quest'ultimo dovrebbe apparire nella riga corrispondente all'account #3465.

In conclusione, se devi occuparti di dati mancanti, invece di paste comando dovresti considerare di usare il join utilità poiché quest'ultima abbinerà le righe in base al loro contenuto e non in base alla posizione nel file di input. Ciò lo rende molto più adatto per applicazioni in stile "database". Ho già pubblicato un video sul join comando, ma probabilmente dovrebbe meritare un articolo a parte, quindi facci sapere se sei interessato a quell'argomento!

6. Pedalando oltre i delimitatori

Nella stragrande maggioranza dei casi d'uso, fornirai un solo carattere come delimitatore. Questo è ciò che abbiamo fatto fino ad ora. Tuttavia, se inserisci più caratteri dopo -d opzione, il comando incolla scorrerà su di essi:il primo carattere verrà utilizzato come primo delimitatore di campo sulla riga, il secondo carattere come secondo delimitatore di campo e così via.

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

I delimitatori di campo possono essere visualizzati solo tra campi. Non alla fine di una riga. E non puoi inserire più di un delimitatore tra due campi dati. Come trucco per superare queste limitazioni, puoi usare /dev/null file speciale come input aggiuntivo in cui è necessario un separatore aggiuntivo:

# 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)

Qualcosa di cui potresti anche abusare:

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

Tuttavia, non c'è bisogno di dire che se raggiungi quel livello di complessità, potrebbe essere un indizio che paste l'utilità non era necessariamente lo strumento migliore per il lavoro. Forse vale la pena considerare, in tal caso, qualcos'altro come sed o comando awk.

Ma cosa succede se l'elenco contiene meno delimitatori del necessario per visualizzare una riga nell'output? È interessante notare che paste il comando "ciclerà" su di loro. Quindi, una volta esaurita la lista, paste il comando tornerà al primo delimitatore, qualcosa che probabilmente apre la porta a un uso creativo. Per quanto mi riguarda, non sono stato in grado di creare nulla di veramente utile con quella funzione dati i miei dati. Quindi dovrai accontentarti del seguente esempio un po' inverosimile. Ma non sarà una completa perdita di tempo poiché quella è stata una buona occasione per menzionare che devi raddoppiare la barra rovesciata (\\ ) quando vuoi usarlo come delimitatore:

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. Delimitatori di caratteri multibyte

Come la maggior parte delle utilità standard di Unix, il comando paste nasce in un momento in cui un carattere equivaleva a un byte. Ma non è più così:oggi molti sistemi utilizzano la codifica a lunghezza variabile UTF-8 per impostazione predefinita. In UTF-8, un carattere può essere rappresentato da 1, 2, 3 o 4 byte. Ciò ci consente di mescolare nello stesso file di testo l'intera varietà di scrittura umana, oltre a tonnellate di simboli ed emoji, mantenendo una compatibilità crescente con la codifica dei caratteri US-ASCII a un byte legacy.

Diciamo ad esempio che vorrei utilizzare il WHITE DIAMOND (◇ U+25C7) come separatore di campo. In UTF-8, questo carattere è codificato utilizzando i tre byte e2 97 87 . Questo carattere potrebbe essere difficile da ottenere dalla tastiera, quindi se vuoi provarlo da solo, ti suggerisco di copiarlo e incollarlo dal blocco di codice qui sotto:

# 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

Abbastanza ingannevole, vero? Invece del diamante bianco previsto, ho il simbolo del "punto interrogativo" (almeno, questo è il modo in cui viene visualizzato sul mio sistema). Non è però un personaggio “casuale”. È il carattere sostitutivo Unicode utilizzato "per indicare problemi quando un sistema non è in grado di eseguire il rendering di un flusso di dati in un simbolo corretto" . Allora, cosa è andato storto?

Ancora una volta, l'esame del contenuto binario grezzo dell'output ci darà alcuni indizi:

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

Abbiamo già avuto l'opportunità di esercitarci con i dump esadecimali sopra, quindi i tuoi occhi ora dovrebbero essere abbastanza acuti da individuare i delimitatori di campo nel flusso di byte. Osservando attentamente, vedrai il separatore di campo dopo che il numero di riga è il byte e2 . Ma se continui le tue indagini, noterai che il secondo separatore di campo è 97 . Non solo paste il comando non ha prodotto il carattere che volevo, ma non utilizzava ovunque lo stesso byte del separatore?!?

Aspetta un attimo:non ti ricorda qualcosa di cui già parliamo? E quei due byte e2 97 , non ti sono in qualche modo familiari? Beh, familiare è probabilmente un po' troppo, ma se salti indietro di qualche paragrafo potresti trovarli menzionati da qualche parte...

Allora hai trovato dov'era? In precedenza, ho detto in UTF-8, il diamante bianco è codificato come i tre byte e2 97 87 . E infatti, paste command ha considerato quella sequenza non come un intero carattere di tre byte, ma come tre indipendenti byte e così, ha utilizzato il primo byte come primo separatore di campo, quindi il secondo byte come secondo separatore di campo.

Ti ho lasciato rieseguire quell'esperimento aggiungendo un'altra colonna nei dati di input; dovresti vedere che il terzo separatore di campo è 87 — il terzo byte della rappresentazione UTF-8 per il diamante bianco.

Ok, questa è la spiegazione:paste il comando accetta solo "caratteri" di un byte come separatore. E questo è particolarmente fastidioso, dal momento che, ancora una volta, non conosco alcun modo per superare questa limitazione se non usando il /dev/null trucco che ti ho già dato:

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

Se hai letto il mio precedente articolo sul cut comando, potresti ricordare che ho avuto problemi simili con l'implementazione GNU di quello strumento. Ma ho notato che in quel momento l'implementazione di OpenBSD teneva correttamente conto di LC_CTYPE impostazione locale per identificare i caratteri multibyte. Per curiosità, ho testato paste comando anche su OpenBSD. Purtroppo, questa volta con lo stesso risultato della mia Debian box, nonostante le specifiche per paste utilità che menziona la variabile di ambiente LC_CTYPE come determinante " la locale per l'interpretazione di sequenze di byte di dati di testo come caratteri (ad esempio, caratteri a byte singolo anziché caratteri multibyte negli argomenti e nei file di input)" . Dalla mia esperienza, tutte le principali implementazioni di paste l'utilità attualmente ignora i caratteri multibyte nell'elenco dei delimitatori e assume separatori di un byte. Ma non affermerò di averlo testato per l'intera varietà delle piattaforme *nix. Quindi, se mi sono perso qualcosa qui, non esitare a utilizzare la sezione commenti per correggermi!

Suggerimento bonus:evitare l'insidia \0

Per ragioni storiche:

I comandi:
incolla -d “\0” …​ incolla -d “” …​
non sono necessariamente equivalenti; quest'ultimo non è specificato da questo volume di IEEE Std 1003.1-2001 e potrebbe causare un errore. Il costrutto '\0' è usato per significare "nessun separatore" perché le versioni storiche di paste non seguivano le linee guida della sintassi e il comando:
incolla -d”” …​
non può essere gestito correttamente da getopt().

Quindi, il modo portatile per incollare i file senza usare un delimitatore è specificare il \0 delimitatore. Questo è alquanto controintuitivo poiché, per molti comandi, \0 indica il carattere NUL, un carattere codificato come un byte composto solo da zeri che non deve entrare in conflitto con alcun contenuto di testo.

Potresti trovare il carattere NUL un utile separatore soprattutto quando i tuoi dati possono contenere caratteri arbitrari (come quando si lavora con nomi di file o dati forniti dall'utente). Sfortunatamente, non sono a conoscenza di alcun modo per utilizzare il carattere NUL come delimitatore di campo con paste comando. Ma forse sai come farlo? In tal caso, sarei più che felice di leggere la tua soluzione nella sezione dei comandi.

D'altra parte, il paste la parte di implementazione di GNU Coreutils ha il non standard -z opzione per passare dalla nuova riga al carattere NUL per il separatore di riga. Ma in tal caso, il carattere NUL verrà utilizzato come separatore di riga entrambi per l'ingresso e l'uscita. Quindi, per testare questa funzionalità, abbiamo prima bisogno di una versione con terminazione zero dei nostri file di input:

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

Per vedere cosa è cambiato nel processo, possiamo usare hexdump utility per esaminare il contenuto binario grezzo dei file:

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|

Ti lascerò confrontare da solo i due dump esadecimali sopra per identificare la differenza tra i file ".zero" e i file di testo originali. Come suggerimento, posso dirti che una nuova riga è codificata come 0a byte.

Si spera che ti sia preso il tempo necessario per individuare il carattere NUL nei file di input ".zero". Ad ogni modo, ora abbiamo una versione con terminazione zero dei file di input, quindi possiamo usare il -z opzione di paste comando per gestire quei dati, producendo nell'output anche un risultato con terminazione zero:

# 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

Poiché i miei file di input non contengono nuove righe incorporate nei dati, -z opzione è di utilità limitata qui. Ma sulla base delle spiegazioni di cui sopra, ti lascio provare a capire perché il seguente esempio funziona "come previsto". Per comprendere appieno che probabilmente è necessario scaricare i file di esempio ed esaminarli a livello di byte utilizzando hexdump utilità come abbiamo fatto sopra:

# 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

Cosa c'è di più?

Il paste comando produce solo output di testo delimitato. Ma come illustrato alla fine del video introduttivo, se il tuo sistema supporta la column BSD utilità, puoi usarlo per ottenere tabelle ben formattate convertendo il paste output del comando in un formato di testo a larghezza fissa. Ma questo sarà oggetto di un prossimo articolo. Quindi resta sintonizzato e, come sempre, non dimenticare di condividere quell'articolo sui tuoi siti Web e social media preferiti!


Linux
  1. Comando Linux Cat:utilizzo ed esempi

  2. Il comando Sed di Linux:utilizzo ed esempi

  3. Il comando AWK di Linux:esempi di sintassi di utilizzo di Linux e Unix

  4. Comando mv in Linux:7 esempi essenziali

  5. Comando Linux df

50 Comando grep produttivo e pratico per gli appassionati di Linux

16 Esempi pratici e utili di Echo Command in Linux

15 esempi pratici di comando Rsync in Linux

5 esempi pratici di Tail Command in Linux

7 Utilizzo essenziale e pratico del comando Incolla in Linux

Cat Command in Linux:esempi essenziali e avanzati