GNU/Linux >> LINUX-Kenntnisse >  >> Linux

7 Grundlegende und praktische Verwendung des Einfügebefehls in Linux

In einem früheren Artikel haben wir über den cut-Befehl gesprochen, der verwendet werden kann, um Spalten aus einer CSV- oder tabellarischen Textdatendatei zu extrahieren.

Das paste Der Befehl macht genau das Gegenteil:Er führt mehrere Eingabedateien zusammen, um daraus eine neue Textdatei mit Trennzeichen zu erstellen. Wir werden sehen, wie man den Paste-Befehl effektiv unter Linux und Unix verwendet.

7 Praktische Beispiele für den Paste-Befehl unter Linux

Wenn Sie Videos bevorzugen, können Sie sich dieses Video ansehen, in dem dieselben Beispiele für den Einfügebefehl erklärt werden, die in diesem Artikel beschrieben wurden.

1. Spalten einfügen

In seinem einfachsten Anwendungsfall, dem paste Befehl dauert N Eingabedateien und fügen Sie sie Zeile für Zeile in die Ausgabe ein:

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

Aber verlassen wir jetzt die theoretischen Erläuterungen, um an einem praktischen Beispiel zu arbeiten. Wenn Sie die im obigen Video verwendeten Beispieldateien heruntergeladen haben, können Sie sehen, dass ich mehrere Datendateien habe, die den verschiedenen Spalten entsprechen einer Tabelle:

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 ist ziemlich einfach, aus diesen Daten eine tabulatorgetrennte Textdatei zu erstellen:

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

Wie Sie vielleicht sehen, erzeugt der Inhalt dieser tabulatorgetrennten Wertedatei bei der Anzeige auf der Konsole keine perfekt formatierte Tabelle. Aber das ist beabsichtigt:das paste Der Befehl wird nicht verwendet, um Textdateien mit fester Breite zu erstellen, sondern nur Textdateien mit Trennzeichen, bei denen einem bestimmten Zeichen die Rolle des Feldtrennzeichens zugewiesen wird.

Also, auch wenn es in der obigen Ausgabe nicht offensichtlich ist, gibt es tatsächlich einen und nur einen Tabulatorzeichen zwischen jedem Feld. Lassen Sie uns das mit dem sed-Befehl verdeutlichen:

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$

Unsichtbare Zeichen werden nun eindeutig in der Ausgabe angezeigt. Und Sie können die Tabulatorzeichen sehen, die als \t angezeigt werden . Sie können sie zählen:In jeder Ausgabezeile befinden sich immer drei Registerkarten – eine zwischen jedem Feld. Und wenn Sie zwei davon hintereinander sehen, bedeutet das nur, dass dort ein leeres Feld war. Dies ist in meinen speziellen Beispieldateien oft der Fall, da in jeder Zeile entweder das Feld CREDIT oder DEBIT gesetzt ist, aber niemals beide gleichzeitig.

2. Ändern des Feldtrennzeichens

Wie wir gesehen haben, das paste Der Befehl verwendet das Tabulatorzeichen als standardmäßiges Feldtrennzeichen („Trennzeichen“). Etwas, das wir mit dem -d ändern können Möglichkeit. Nehmen wir an, ich möchte stattdessen ein Semikolon verwenden:

# 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

sed muss nicht angehängt werden Befehl am Ende der Pipeline hier, da das verwendete Trennzeichen ein druckbares Zeichen ist. Wie auch immer, das Ergebnis ist das gleiche:In einer gegebenen Zeile wird jedes Feld von seinem Nachbarn getrennt, indem ein einstelliges Trennzeichen verwendet wird.

3. Transponieren von Daten im seriellen Modus

Die obigen Beispiele haben eines gemeinsam:das paste Der Befehl liest alle seine Eingabedateien parallel, was erforderlich ist, damit er sie Zeile für Zeile in der Ausgabe zusammenführen kann.

Aber das paste Der Befehl kann auch im sogenannten seriellen Modus arbeiten , aktiviert mit -s Flagge. Wie der Name schon sagt, im seriellen Modus das paste Der Befehl liest die Eingabedateien nacheinander. Der Inhalt der ersten Eingabedatei wird verwendet, um die erste Ausgabezeile zu erzeugen. Dann wird der Inhalt der zweiten Eingabedatei verwendet, um die zweite Ausgabezeile zu erzeugen, und so weiter. Das bedeutet auch, dass die Ausgabe so viele Zeilen haben wird, wie Dateien in der Eingabe waren.

Genauer gesagt, die Daten aus der Datei N erscheint als N Zeile in der Ausgabe im seriellen Modus, während es als N erscheinen würde Spalte im voreingestellten „Parallel“-Modus. Mathematisch ausgedrückt ist die im seriellen Modus erhaltene Tabelle die Transponierte der im Standardmodus erstellten Tabelle (und umgekehrt). ).

Betrachten wir zur Veranschaulichung eine kleine Teilstichprobe unserer Daten:

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

Im Standardmodus („parallel“) dienen die Daten der Eingabedatei als Spalten in der Ausgabe, wodurch eine Tabelle mit zwei Spalten und fünf Zeilen erstellt wird:

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

Aber im seriellen Modus erscheinen die Daten der Eingabedatei als Zeilen, was nun eine Tabelle mit fünf Spalten und zwei Zeilen erzeugt:

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

4. Arbeiten mit der Standardeingabe

Wie viele Standard-Utilities ist auch das paste Der Befehl kann die Standardeingabe zum Lesen von Daten verwenden. Entweder implizit, wenn kein Dateiname als Argument angegeben ist, oder explizit durch Verwendung des speziellen - Dateinamen. Anscheinend ist das aber nicht so nützlich:

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

Ich ermutige Sie, es selbst zu testen, aber die folgende Syntax sollte das gleiche Ergebnis liefern – was wiederum den Einfügebefehl in diesem Fall unbrauchbar macht:

head -5 ACCOUNTLIB.csv | paste -

Was könnte also der Sinn sein, Daten aus der Standardeingabe zu lesen? Nun, mit dem -s Flagge, die Dinge werden viel interessanter, wie wir es jetzt sehen werden.

4.1. Zeilen einer Datei verbinden

Wie wir ein paar Absätze zuvor gesehen haben, schreibt der Einfügebefehl im seriellen Modus alle Zeilen einer Eingabedatei in dieselbe Ausgabezeile. Dies gibt uns eine einfache Möglichkeit, alle von der Standardeingabe gelesenen Zeilen in nur einer (möglicherweise sehr langen) Ausgabezeile zu verbinden:

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

Dies ist größtenteils dasselbe, was Sie mit tr tun könnten Befehl, aber mit einem Unterschied. Lassen Sie uns den diff verwenden Dienstprogramm, um das zu erkennen:

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

Wie vom diff gemeldet Dienstprogramm, können wir den tr sehen Der Befehl hat jede Instanz des Newline-Zeichens durch das angegebene Trennzeichen ersetzt, einschließlich des allerletzten. Andererseits das paste Der Befehl ließ das letzte Zeilenumbruchzeichen unberührt. Je nachdem, ob Sie das Trennzeichen nach dem allerletzten Feld benötigen oder nicht, verwenden Sie den einen oder anderen Befehl.

4.2. Mehrspaltige Formatierung einer Eingabedatei

Gemäß den Spezifikationen der Open Group „die Standardeingabe soll zeilenweise gelesen werden“ durch paste Befehl. Übergeben Sie also mehrere Vorkommen des - spezieller Dateiname als Argumente für paste Befehl führt dazu, dass so viele aufeinanderfolgende Zeilen der Eingabe in dieselbe Ausgabezeile geschrieben werden:

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

Um die Dinge klarer zu machen, ermutige ich Sie, den Unterschied zwischen den beiden folgenden Befehlen zu studieren. Im ersten Fall öffnet der Einfügebefehl dreimal dieselbe Datei, was zu einer Datenduplizierung in der Ausgabe führt. Andererseits wird im zweiten Fall die ACCOUNTLIB-Datei nur einmal geöffnet (von der Shell), aber dreimal für jede Zeile gelesen (von paste Befehl), was dazu führt, dass der Dateiinhalt in drei Spalten angezeigt wird:

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

Angesichts des Verhaltens von paste Befehl beim Lesen von der Standardeingabe, ist es normalerweise nicht ratsam mehrere - zu verwenden spezielle Dateinamen im seriellen Modus. In diesem Fall würde das erste Vorkommen die Standardeingabe bis zu ihrem Ende und die nachfolgenden Vorkommen von - lesen würde aus einem bereits erschöpften Eingabestrom lesen – was dazu führt, dass keine Daten mehr verfügbar sind:

# 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. Arbeiten mit Dateien unterschiedlicher Länge

Die Open Group-Spezifikationen für das paste Nutzen sind ganz klar:

Wenn bei einer oder mehreren Eingabedateien, aber nicht bei allen Eingabedateien eine Dateiende-Bedingung erkannt wird, soll sich das Einfügen so verhalten, als ob leere Zeilen aus den Dateien gelesen wurden, bei denen das Dateiende erkannt wurde, es sei denn, die -s Option ist angegeben.

Das Verhalten entspricht also dem, was Sie erwarten können:Fehlende Daten werden durch „leeren“ Inhalt ersetzt. Um dieses Verhalten zu veranschaulichen, zeichnen wir ein paar weitere Transaktionen in unserer „Datenbank“ auf. Um die Originaldateien intakt zu halten, arbeiten wir jedoch an einer Kopie unserer Daten:

# 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

Mit diesen Aktualisierungen haben wir jetzt eine neue Kapitalbewegung von Konto Nr. 1080 zu Konto Nr. 4356 registriert. Wie Sie jedoch vielleicht bemerkt haben, habe ich mich nicht darum gekümmert, die ACCOUNTLIB-Datei zu aktualisieren. Dies scheint kein so großes Problem zu sein, da das paste Der Befehl ersetzt die fehlenden Zeilen durch leere Daten:

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

Aber Vorsicht, das paste Befehl kann Zeilen nur nach ihrem physischen abgleichen Position:Es kann nur sagen, dass eine Datei „kürzer“ ist als eine andere. Nicht wo die Daten fehlen. Daher werden am Ende der Ausgabe immer die Leerfelder hinzugefügt, was zu unerwarteten Offsets in Ihren Daten führen kann. Machen wir das deutlich, indem wir noch eine weitere Transaktion hinzufügen:

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

Dieses Mal war ich strenger, da ich sowohl die Kontonummer (ACCOUNTNUM) als auch die entsprechende Bezeichnung (ACCOUNTLIB) sowie die CREDIT- und DEBIT-Datendateien ordnungsgemäß aktualisiert habe. Da aber im vorherigen Datensatz Daten fehlten, muss paste Der Befehl kann die zugehörigen Felder nicht mehr in derselben Zeile halten:

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;

Wie Sie vielleicht sehen, wird das Konto Nr. 4356 mit dem Label „WEB HOSTING“ gemeldet, während letzteres in Wirklichkeit in der Zeile erscheinen sollte, die dem Konto Nr. 3465 entspricht.

Als Abschluss, wenn Sie mit fehlenden Daten umgehen müssen, statt des paste Befehl sollten Sie die Verwendung von join in Erwägung ziehen Dienstprogramm, da letzteres Zeilen basierend auf ihrem Inhalt abgleicht und nicht basierend auf ihrer Position in der Eingabedatei. Das macht es viel besser geeignet für Anwendungen im „Datenbank“-Stil. Ich habe bereits ein Video zum join veröffentlicht Befehl, aber das sollte wahrscheinlich einen eigenen Artikel verdienen, also lassen Sie uns wissen, wenn Sie an diesem Thema interessiert sind!

6. Wechseln über Trennzeichen

In der überwiegenden Mehrheit der Anwendungsfälle werden Sie nur ein Zeichen als Trennzeichen angeben. Das haben wir bis jetzt getan. Wenn Sie jedoch nach dem -d mehrere Zeichen eingeben Option, wird der Einfügebefehl sie durchlaufen:Das erste Zeichen wird als erstes Feldtrennzeichen in der Zeile verwendet, das zweite Zeichen als zweites Feldtrennzeichen und so weiter.

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

Feldtrennzeichen dürfen nur zwischen stehen Felder. Nicht am Zeilenende. Und Sie können nicht mehr als ein Trennzeichen zwischen zwei gegebenen Feldern einfügen. Als Trick, um diese Einschränkungen zu umgehen, können Sie /dev/null verwenden spezielle Datei als zusätzliche Eingabe, wo Sie ein zusätzliches Trennzeichen benötigen:

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

Etwas, das Sie vielleicht sogar missbrauchen:

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

Es ist jedoch unnötig zu sagen, wenn Sie dieses Komplexitätsniveau erreichen, kann es ein Hinweis auf das paste sein Dienstprogramm war nicht unbedingt das beste Werkzeug für den Job. Vielleicht eine Überlegung wert, in diesem Fall etwas anderes wie sed oder awk-Befehl.

Was aber, wenn die Liste weniger Trennzeichen enthält, als für die Anzeige einer Zeile in der Ausgabe erforderlich sind? Interessanterweise das paste Der Befehl „zyklisiert“ sie. Also, sobald die Liste erschöpft ist, das paste Der Befehl springt zurück zum ersten Trennzeichen, was wahrscheinlich die Tür zu kreativer Nutzung öffnet. Ich selbst konnte mit dieser Funktion angesichts meiner Daten nichts wirklich Nützliches machen. Sie müssen sich also mit dem folgenden, etwas weit hergeholten Beispiel begnügen. Aber es wird keine völlige Zeitverschwendung sein, da dies eine gute Gelegenheit war zu erwähnen, dass Sie den umgekehrten Schrägstrich verdoppeln müssen (\\ ), wenn Sie es als Trennzeichen verwenden möchten:

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. Trennzeichen für Multibyte-Zeichen

Wie die meisten Standard-Unix-Dienstprogramme wird der Einfügebefehl zu einem Zeitpunkt geboren, an dem ein Zeichen einem Byte entspricht. Dies ist jedoch nicht mehr der Fall:Viele Systeme verwenden heute standardmäßig die UTF-8-Codierung mit variabler Länge. In UTF-8 kann ein Zeichen durch 1, 2, 3 oder 4 Bytes dargestellt werden. Auf diese Weise können wir in derselben Textdatei die gesamte Vielfalt menschlicher Schrift – sowie Tonnen von Symbolen und Emojis – mischen und gleichzeitig die aufsteigende Kompatibilität mit der alten Ein-Byte-US-ASCII-Zeichencodierung aufrechterhalten.

Nehmen wir zum Beispiel an, ich möchte den WHITE DIAMOND (◇ U+25C7) als Feldtrenner verwenden. In UTF-8 wird dieses Zeichen mit den drei Bytes e2 97 87 codiert . Dieses Zeichen ist möglicherweise schwer über die Tastatur zu erhalten. Wenn Sie es also selbst versuchen möchten, schlage ich vor, dass Sie es aus dem folgenden Codeblock kopieren und einfügen:

# 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

Ziemlich trügerisch, nicht wahr? Anstelle der erwarteten weißen Raute habe ich dieses „Fragezeichen“-Symbol (zumindest wird es so auf meinem System angezeigt). Es ist jedoch kein "zufälliges" Zeichen. Es ist das Unicode-Ersatzzeichen, das verwendet wird „um auf Probleme hinzuweisen, wenn ein System einen Datenstrom nicht in ein korrektes Symbol umwandeln kann“ . Also, was ist schief gelaufen?

Noch einmal, die Untersuchung des rohen binären Inhalts der Ausgabe wird uns einige Hinweise geben:

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

Wir hatten oben bereits die Gelegenheit, mit Hex-Dumps zu üben, also sollten Ihre Augen jetzt genug geschärft sein, um die Feldbegrenzer im Bytestrom zu erkennen. Bei genauem Hinsehen erkennen Sie als Feldtrenner hinter der Zeilennummer das Byte e2 . Aber wenn Sie Ihre Untersuchungen fortsetzen, werden Sie feststellen, dass das zweite Feldtrennzeichen 97 ist . Nicht nur das paste Befehl nicht das gewünschte Zeichen ausgegeben, aber auch nicht überall das gleiche Byte als Trennzeichen verwendet?!?

Moment mal:Erinnert Sie das nicht an etwas, worüber wir bereits sprechen? Und diese zwei Bytes e2 97 , kommen sie dir nicht irgendwie bekannt vor? Nun, vertraut ist wahrscheinlich ein bisschen zu viel, aber wenn Sie ein paar Absätze zurückspringen, finden Sie sie vielleicht irgendwo erwähnt…

Hast du also gefunden, wo es war? Zuvor habe ich gesagt, dass in UTF-8 die weiße Raute als die drei Bytes e2 97 87 codiert ist . Und tatsächlich, das paste Der Befehl hat diese Sequenz nicht als ganzes Drei-Byte-Zeichen betrachtet, sondern als drei unabhängige Bytes und so wurde das erste Byte als erstes Feldtrennzeichen verwendet, dann das zweite Byte als zweites Feldtrennzeichen.

Ich lasse Sie dieses Experiment erneut ausführen, indem Sie den Eingabedaten eine weitere Spalte hinzufügen. Als drittes Feldtrennzeichen sollte 87 angezeigt werden — das dritte Byte der UTF-8-Darstellung für die weiße Raute.

Ok, das ist die Erklärung:das paste Der Befehl akzeptiert nur Ein-Byte-„Zeichen“ als Trennzeichen. Und das ist besonders ärgerlich, da ich noch einmal keine Möglichkeit kenne, diese Einschränkung zu umgehen, außer durch die Verwendung von /dev/null Trick, den ich dir schon gegeben habe:

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

Wenn Sie meinen vorherigen Artikel über den cut lesen Sie erinnern sich vielleicht, dass ich ähnliche Probleme mit der GNU-Implementierung dieses Tools hatte. Aber ich bemerkte damals, dass die OpenBSD-Implementierung den LC_CTYPE korrekt berücksichtigte Gebietsschemaeinstellung zum Identifizieren von Multibyte-Zeichen. Aus Neugier habe ich das paste getestet Befehl auch auf OpenBSD. Leider diesmal mit dem gleichen Ergebnis wie auf meiner Debian-Box, trotz der Vorgaben zum paste Dienstprogramm, das die Umgebungsvariable LC_CTYPE als Bestimmung “ des Gebietsschemas für die Interpretation von Folgen von Bytes von Textdaten als Zeichen erwähnt (z. B. Einzelbyte- im Gegensatz zu Mehrbyte-Zeichen in Argumenten und Eingabedateien)“ . Aus meiner Erfahrung sind alle wichtigen Implementierungen des paste Das Dienstprogramm ignoriert derzeit Multibyte-Zeichen in der Trennzeichenliste und geht von Ein-Byte-Trennzeichen aus. Aber ich will nicht behaupten, das für die ganze Vielfalt der *nix-Plattformen getestet zu haben. Wenn ich also hier etwas übersehen habe, zögern Sie nicht, den Kommentarbereich zu verwenden, um mich zu korrigieren!

Bonus-Tipp:Vermeidung der \0-Falle

Aus historischen Gründen:

Die Befehle:
paste -d „\0“ …​ paste -d „“ …​
sind nicht unbedingt gleichwertig; Letzteres ist in diesem Band von IEEE Std 1003.1-2001 nicht spezifiziert und kann zu einem Fehler führen. Das Konstrukt „\0“ wird verwendet, um „kein Trennzeichen“ zu bedeuten, da historische Versionen des Einfügens nicht den Syntaxrichtlinien und dem Befehl folgten:
paste -d”” …​
konnte von getopt() nicht richtig verarbeitet werden.

Die portable Methode zum Einfügen von Dateien ohne Verwendung eines Trennzeichens besteht also darin, den \0 anzugeben Trennzeichen. Dies ist etwas kontraintuitiv, da für viele Befehle \0 bedeutet das NUL-Zeichen – ein als Byte codiertes Zeichen, das nur aus Nullen besteht und nicht mit Textinhalten kollidieren sollte.

Sie finden das NUL-Zeichen möglicherweise ein nützliches Trennzeichen, insbesondere wenn Ihre Daten beliebige Zeichen enthalten können (z. B. wenn Sie mit Dateinamen oder vom Benutzer bereitgestellten Daten arbeiten). Leider ist mir keine Möglichkeit bekannt, das NUL-Zeichen als Feldtrenner beim paste zu verwenden Befehl. Aber weißt du vielleicht, wie das geht? Wenn das der Fall ist, würde ich mich sehr freuen, Ihre Lösung im Befehlsabschnitt zu lesen.

Andererseits das paste Der Implementierungsteil der GNU Coreutils hat den nicht standardmäßigen -z Option zum Umschalten vom Zeilenumbruch auf das NUL-Zeichen für das Zeilentrennzeichen. Aber in diesem Fall wird das NUL-Zeichen als Zeilentrenner verwendet beide für die Ein- und Ausgabe. Um diese Funktion zu testen, benötigen wir also zuerst eine nullterminierte Version unserer Eingabedateien:

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

Um zu sehen, was sich im Prozess geändert hat, können wir den hexdump verwenden Dienstprogramm, um den reinen Binärinhalt der Dateien zu untersuchen:

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|

Ich lasse Sie die beiden Hex-Dumps oben selbst vergleichen, um den Unterschied zwischen „.zero“-Dateien und den ursprünglichen Textdateien zu identifizieren. Als Hinweis kann ich Ihnen sagen, dass ein Zeilenumbruch als 0a codiert ist Byte.

Hoffentlich haben Sie sich die Zeit genommen, das NUL-Zeichen in den „.zero“-Eingabedateien zu finden. Jedenfalls haben wir jetzt eine nullterminierte Version der Eingabedateien, sodass wir das -z verwenden können Option des paste Befehl, um diese Daten zu verarbeiten, wobei in der Ausgabe auch ein nullterminiertes Ergebnis erzeugt wird:

# 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

Da meine Eingabedateien keine eingebetteten Zeilenumbrüche in den Daten enthalten, muss das -z Option ist hier von begrenztem Nutzen. Aber basierend auf den obigen Erklärungen lasse ich Sie versuchen zu verstehen, warum das folgende Beispiel „wie erwartet“ funktioniert. Um dies vollständig zu verstehen, müssen Sie wahrscheinlich die Beispieldateien herunterladen und sie mit dem hexdump auf Byte-Ebene untersuchen Dienstprogramm, wie wir es oben getan haben:

# 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

Was ist mehr?

Das paste Der Befehl erzeugt nur eine Textausgabe mit Trennzeichen. Aber wie am Ende des Einführungsvideos gezeigt, wenn Ihr System die BSD-Spalte column unterstützt Dienstprogramm, können Sie es verwenden, um schön formatierte Tabellen zu erhalten, indem Sie paste umwandeln Befehlsausgabe in ein Textformat mit fester Breite. Aber das wird das Thema eines kommenden Artikels sein. Bleiben Sie also auf dem Laufenden und vergessen Sie wie immer nicht, diesen Artikel auf Ihren bevorzugten Websites und in den sozialen Medien zu teilen!


Linux
  1. Linux-Cat-Befehl:Verwendung und Beispiele

  2. Der Linux-Sed-Befehl:Verwendung und Beispiele

  3. Linux-Befehle:jobs, bg und fg

  4. Der Linux-AWK-Befehl – ​​Syntaxbeispiele für Linux und Unix

  5. Linux-df-Befehl

50 Produktiver und praktischer grep-Befehl für Linux-Enthusiasten

16 Praktische und nützliche Beispiele für Echo Command unter Linux

15 Praktische Beispiele für den Rsync-Befehl unter Linux

5 praktische Beispiele für Tail-Befehle unter Linux

7 Grundlegende und praktische Verwendung des Einfügebefehls in Linux

Cat-Befehl in Linux:Wesentliche und erweiterte Beispiele