GNU/Linux >> Znalost Linux >  >> Linux

7 Základní a praktické použití příkazu Paste v Linuxu

V předchozím článku jsme hovořili o příkazu cut, který lze použít k extrahování sloupců z datového souboru CSV nebo tabulkového textového souboru.

paste příkaz dělá přesný opak:sloučí několik vstupních souborů a vytvoří z nich nový textový soubor s oddělovači. Uvidíme, jak efektivně používat příkaz Paste v Linuxu a Unixu.

7 Praktické příklady příkazu Paste v Linuxu

Pokud dáváte přednost videím, můžete se podívat na toto video vysvětlující stejné příklady příkazu Vložit, o kterých se mluví v tomto článku.

1. Vkládání sloupců

V jeho nejzákladnějším případě použití je paste příkaz trvá N vstupní soubory a spojte je řádek po řádku na výstupu:

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

Ale nechme nyní teoretická vysvětlení, abychom pracovali na praktickém příkladu. Pokud jste si stáhli ukázkové soubory použité ve videu výše, můžete vidět, že mám několik datových souborů odpovídajících různým sloupcům tabulky:

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

Z těchto dat je docela snadné vytvořit textový soubor oddělený tabulátory:

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

Jak můžete vidět, když je obsah tohoto souboru hodnot oddělených tabulátory zobrazen na konzole, nevytváří dokonale formátovanou tabulku. Ale toto je záměrné:paste příkaz se nepoužívá k vytváření textových souborů s pevnou šířkou, ale pouze textových souborů s oddělovači, kde je jednomu danému znaku přiřazena role oddělovače polí.

Takže, i když to není z výše uvedeného výstupu zřejmé, ve skutečnosti existuje jeden a pouze jeden znak tabulátoru mezi jednotlivými poli. Ukažme si to pomocí příkazu 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$

Nyní se neviditelné znaky ve výstupu zobrazují jednoznačně. A můžete vidět znaky tabulátoru zobrazené jako \t . Můžete je spočítat:na každém výstupním řádku jsou vždy tři záložky – jedna mezi každým polem. A když je vidíte dva za sebou, znamená to jen, že tam bylo prázdné pole. To je často případ mých konkrétních ukázkových souborů, protože na každém řádku je nastaveno buď pole CREDIT nebo DEBIT, ale nikdy není obojí současně.

2. Změna oddělovače pole

Jak jsme viděli, paste příkaz používá znak tabulátoru jako výchozí oddělovač polí („oddělovač“). Něco, co můžeme změnit pomocí -d volba. Řekněme, že bych místo toho chtěl použít středník:

# 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

Není třeba přidávat sed příkaz na konci potrubí zde, protože oddělovač, který jsme použili, je tisknutelný znak. Výsledek je každopádně stejný:na daném řádku je každé pole odděleno od sousedního pomocí jednoznakového oddělovače.

3. Transponování dat pomocí sériového režimu

Výše uvedené příklady mají jednu věc společnou:paste příkaz čte všechny své vstupní soubory paralelně, což je vyžadováno, aby je mohl sloučit řádek po řádku do výstupu.

Ale paste příkaz může také pracovat v takzvaném sériovém režimu , povoleno pomocí -s vlajka. Jak již název napovídá, v sériovém režimu paste příkaz bude číst vstupní soubory jeden po druhém. Obsah prvního vstupního souboru bude použit k vytvoření prvního výstupního řádku. Potom bude obsah druhého vstupního souboru použit k vytvoření druhého výstupního řádku a tak dále. To také znamená, že výstup bude mít tolik řádků, kolik bylo souborů na vstupu.

Formálněji jde o údaje převzaté ze souboru N se zobrazí jako N řádek na výstupu v sériovém režimu, zatímco se zobrazí jako N sloupec ve výchozím „paralelním“ režimu. Z matematického hlediska je tabulka získaná v sériovém režimu transpozicí tabulky vytvořené ve výchozím režimu (a naopak ).

Abychom to ilustrovali, podívejme se na malý dílčí vzorek našich dat:

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

Ve výchozím („paralelním“) režimu budou data vstupního souboru sloužit jako sloupce na výstupu, čímž vznikne tabulka se dvěma sloupci a pěti řádky:

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

Ale v sériovém režimu se data vstupního souboru objeví jako řádky, takže nyní vznikne tabulka s pěti sloupci po dvou řádcích:

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

4. Práce se standardním vstupem

Stejně jako mnoho standardních nástrojů, paste příkaz může použít standardní vstup ke čtení dat. Buď implicitně, když není jako argument uveden žádný název souboru, nebo explicitně pomocí speciálního - název souboru. Zjevně to však není tak užitečné:

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

Doporučuji vám, abyste si to vyzkoušeli sami, ale následující syntaxe by měla přinést stejný výsledek – v takovém případě bude příkaz paste opět zbytečný:

head -5 ACCOUNTLIB.csv | paste -

Jaký by tedy mohl být smysl čtení dat ze standardního vstupu? No, s -s vlajka, věci se stanou mnohem zajímavějšími, jak to nyní uvidíme.

4.1. Spojení řádků souboru

Jak jsme viděli o pár odstavců dříve, v sériovém režimu příkaz paste zapíše všechny řádky vstupního souboru na stejný výstupní řádek. To nám dává jednoduchý způsob, jak spojit všechny řádky načtené ze standardního vstupu pouze do jednoho (potenciálně velmi dlouhého) výstupního řádku:

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

To je většinou totéž, co byste mohli udělat pomocí tr příkaz, ale s jedním rozdílem. Použijme diff nástroj, který to odhalí:

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

Jak uvádí diff utility, můžeme vidět tr příkaz nahradil každý výskyt znaku nového řádku daným oddělovačem, včetně toho úplně posledního. Na druhé straně paste příkaz ponechal poslední znak nového řádku nedotčený. Takže podle toho, zda potřebujete oddělovač za posledním polem nebo ne, použijete jeden nebo druhý příkaz.

4.2. Vícesloupcové formátování jednoho vstupního souboru

Podle specifikací Open Group „standardní vstup se bude číst jeden řádek po druhém“ paste příkaz. Takže předání několika výskytů - speciální název souboru jako argumenty pro paste příkaz bude mít za následek zapsání tolika po sobě jdoucích řádků vstupu do stejného výstupního řádku:

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

Aby to bylo jasnější, doporučuji vám prostudovat si rozdíl mezi dvěma níže uvedenými příkazy. V prvním případě příkaz vložit otevře třikrát stejný soubor, což má za následek duplikaci dat ve výstupu. Na druhou stranu ve druhém případě se soubor ACCOUNTLIB otevře pouze jednou (prostřednictvím shellu), ale pro každý řádek se čte třikrát (pomocí paste příkaz), což má za následek zobrazení obsahu souboru ve třech sloupcích:

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

Vzhledem k chování paste při čtení ze standardního vstupu to obvykle není doporučujeme použít několik - speciální názvy souborů v sériovém režimu. V takovém případě by první výskyt četl standardní vstup až do jeho konce a následné výskyty - by četl z již vyčerpaného vstupního toku – což vedlo k tomu, že již nejsou k dispozici žádná další data:

# 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. Práce se soubory různé délky

Specifikace otevřené skupiny pro paste utility jsou celkem jasné:

Pokud je stav konce souboru detekován u jednoho nebo více vstupních souborů, ale ne u všech vstupních souborů, bude se vkládání chovat tak, jako by byly prázdné řádky načteny ze souborů, na kterých byl detekován konec souboru, pokud Je zadána volba -s.

Chování je tedy takové, jaké můžete očekávat:chybějící data jsou nahrazena „prázdným“ obsahem. Abychom toto chování ilustrovali, zaznamenejme do naší „databáze“ několik dalších transakcí. Abychom zachovali původní soubory nedotčené, budeme pracovat na kopii našich dat:

# 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

Díky těmto aktualizacím jsme nyní zaregistrovali nový pohyb kapitálu z účtu #1080 na účet #4356. Nicméně, jak jste si toho mohli všimnout, neobtěžoval jsem se aktualizovat soubor ACCOUNTLIB. To se nezdá být tak velký problém, protože paste příkaz nahradí chybějící řádky prázdnými daty:

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

Ale pozor, paste příkaz může porovnat řádky pouze podle jejich fyzického pozice:vše, co dokáže, je, že soubor je „kratší“ než jiný. Ne kde údaje chybí. Takže vždy přidá prázdná pole na konec výstupu, což může způsobit neočekávané posuny ve vašich datech. Ujasněme si to přidáním další transakce:

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

Tentokrát jsem byl přísnější, protože jsem řádně aktualizoval jak číslo účtu (ACCOUNTNUM), tak jeho odpovídající štítek (ACCOUNTLIB) a také datové soubory CREDIT a DEBIT. Ale protože v předchozím záznamu chyběla data, paste příkaz již není schopen udržovat související pole na stejném řádku:

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;

Jak můžete vidět, účet #4356 je hlášen se štítkem „WEB HOSTING“, zatímco ve skutečnosti by se tento účet měl objevit na řádku odpovídajícímu účtu #3465.

Na závěr, pokud se musíte vypořádat s chybějícími daty, místo paste měli byste zvážit použití join nástroj, protože ten bude odpovídat řádkům na základě jejich obsahu, a nikoli na základě pozice ve vstupním souboru. Díky tomu je mnohem vhodnější pro aplikace ve stylu „databáze“. Již jsem zveřejnil video o join příkaz, ale to by si pravděpodobně zasloužilo samostatný článek, takže pokud vás toto téma zajímá, dejte nám vědět!

6. Cyklování přes oddělovače

V drtivé většině případů použití uvedete jako oddělovač pouze jeden znak. To je to, co jsme dosud dělali. Pokud však za -d zadáte několik znaků příkaz paste je bude cyklicky přecházet:první znak bude použit jako první oddělovač pole na řádku, druhý znak jako druhý oddělovač pole atd.

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

Oddělovače polí se mohou objevit pouze mezi pole. Ne na konci řádku. A mezi dvě daná pole nemůžete vložit více než jeden oddělovač. Jako trik k překonání těchto omezení můžete použít /dev/null speciální soubor jako další vstup, kde potřebujete další oddělovač:

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

Něco, co můžete i zneužít:

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

Není však třeba říkat, že pokud dosáhnete této úrovně složitosti, může to být vodítkem paste utility nebyly nutně tím nejlepším nástrojem pro tuto práci. Možná by stálo za zvážení v tom případě něco jiného jako sed nebo příkaz awk.

Co když ale seznam obsahuje méně oddělovačů, než je potřeba k zobrazení řádku ve výstupu? Zajímavé je, že paste příkaz bude „cyklovat“ nad nimi. Jakmile je seznam vyčerpán, paste příkaz skočí zpět na první oddělovač, což pravděpodobně otevírá dveře nějakému kreativnímu použití. Pokud jde o mě, nebyl jsem schopen s touto funkcí udělat nic skutečně užitečného vzhledem k mým datům. Budete se tedy muset spokojit s následujícím trochu přitaženým příkladem. Ale nebude to úplná ztráta času, protože to byla dobrá příležitost zmínit, že musíte zdvojnásobit zpětné lomítko (\\ ), když jej chcete použít jako oddělovač:

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. Vícebajtové oddělovače znaků

Stejně jako většina standardních unixových nástrojů se příkaz vložit rodí v okamžiku, kdy jeden znak odpovídá jednomu bajtu. To už ale neplatí:dnes mnoho systémů standardně používá kódování s proměnnou délkou UTF-8. V UTF-8 může být znak reprezentován 1, 2, 3 nebo 4 bajty. To nám umožňuje smíchat ve stejném textovém souboru celou řadu lidských písem – stejně jako tuny symbolů a emotikonů – při zachování vzestupné kompatibility se starším jednobajtovým kódováním znaků US-ASCII.

Řekněme například, že bych chtěl použít jako oddělovač pole BÍLÝ DIAMANT (◇ U+25C7). V UTF-8 je tento znak zakódován pomocí tří bajtů e2 97 87 . Tento znak může být obtížné získat z klávesnice, takže pokud si to chcete vyzkoušet sami, doporučuji zkopírovat a vložit jej z bloku kódu níže:

# 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

Docela klamné, že? Místo očekávaného bílého kosočtverce mám symbol „otazník“ (alespoň tak se zobrazuje v mém systému). Není to však „náhodná“ postava. Jedná se o náhradní znak Unicode používaný „k označení problémů, když systém není schopen převést proud dat do správného symbolu“ . Takže, co se pokazilo?

Zkoumání surového binárního obsahu výstupu nám opět poskytne určité vodítko:

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

Výše jsme již měli možnost procvičovat si hex výpisy, takže vaše oči by nyní měly být dostatečně zostřené, abyste zachytili oddělovače polí v toku bajtů. Když se podíváte pozorně, uvidíte, že oddělovač polí za číslem řádku je bajt e2 . Pokud však budete pokračovat ve vyšetřování, všimnete si, že druhý oddělovač polí je 97 . Nejen paste příkaz nevypsal znak, který jsem chtěl, ale také nepoužíval všude stejný bajt jako oddělovač?!?

Počkejte chvíli:nepřipomíná vám to něco, o čem jsme již mluvili? A ty dva bajty e2 97 , nejsou vám trochu povědomé? Dobře, povědomost je asi trochu moc, ale když přeskočíte o pár odstavců zpět, možná je někde najdete…

Tak jsi našel kde to bylo? Dříve jsem řekl v UTF-8, bílý kosočtverec je zakódován jako tři bajty e2 97 87 . A skutečně, paste příkaz nepovažoval tuto sekvenci za celý tříbajtový znak, ale za tři nezávislé bajtů a tak použil první bajt jako první oddělovač pole a poté druhý bajt jako oddělovač druhého pole.

Nechal jsem vás znovu spustit tento experiment přidáním dalšího sloupce do vstupních dat; měli byste vidět oddělovač třetího pole 87 — třetí bajt reprezentace UTF-8 pro bílý diamant.

Dobře, to je vysvětlení:paste příkaz přijímá jako oddělovač pouze jednobajtové „znaky“. A to je obzvláště nepříjemné, protože opět nevím, jak toto omezení překonat jinak než pomocí /dev/null trik, který jsem vám již dal:

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

Pokud jste si přečetli můj předchozí článek o cut možná si pamatujete, že jsem měl podobné problémy s implementací GNU tohoto nástroje. Všiml jsem si ale, že v té době implementace OpenBSD správně zohledňovala LC_CTYPE nastavení národního prostředí pro identifikaci vícebajtových znaků. Ze zvědavosti jsem otestoval paste příkaz také na OpenBSD. Bohužel, tentokrát se stejným výsledkem jako na mém boxu s Debianem, navzdory specifikacím pro paste obslužný program zmiňující proměnnou prostředí LC_CTYPE jako určující ” národní prostředí pro interpretaci sekvencí bajtů textových dat jako znaků (například jednobajtových na rozdíl od vícebajtových znaků v argumentech a vstupních souborech)“ . Z mé zkušenosti, všechny hlavní implementace paste obslužný program aktuálně ignoruje vícebajtové znaky v seznamu oddělovačů a předpokládá jednobajtové oddělovače. Ale nebudu tvrdit, že jsem to testoval pro celou řadu platforem *nix. Takže pokud jsem zde něco přehlédl, neváhejte mě opravit pomocí sekce komentářů!

Bonusový tip:Jak se vyhnout nástrahám \0

Z historických důvodů:

Příkazy:
vložit -d “\0” …​ vložit -d “” …​
nejsou nutně ekvivalentní; druhý jmenovaný není specifikován tímto svazkem IEEE Std 1003.1-2001 a může vést k chybě. Konstrukt ‚\0‘ se používá ve významu „bez oddělovače“, protože historické verze paste nedodržovaly pokyny pro syntax a příkaz:
vložit -d”” …​
nebylo možné správně zpracovat getopt().

Přenosným způsobem vkládání souborů bez použití oddělovače je tedy zadání \0 oddělovač. To je poněkud neintuitivní, protože pro mnoho příkazů je to \0 znamená znak NUL – znak zakódovaný jako bajt tvořený pouze nulami, který by neměl kolidovat s žádným textovým obsahem.

Znak NUL může být užitečným oddělovačem, zejména pokud vaše data mohou obsahovat libovolné znaky (například při práci s názvy souborů nebo daty poskytnutými uživateli). Bohužel nevím o žádném způsobu, jak použít znak NUL jako oddělovač pole pomocí příkazu paste příkaz. Ale možná víte, jak na to? Pokud je to tak, velmi rád si přečtu vaše řešení v sekci příkazů.

Na druhé straně paste implementační část GNU Coreutils má nestandardní -z možnost přepnutí z nového řádku na znak NUL pro oddělovač řádků. Ale v tom případě bude jako oddělovač řádku použit znak NUL obojí pro vstup a výstup. Abychom tuto funkci otestovali, potřebujeme nejprve verzi našich vstupních souborů s nulovým zakončením:

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

Abychom viděli, co se v procesu změnilo, můžeme použít hexdump nástroj pro prozkoumání surového binárního obsahu souborů:

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|

Nechám vás porovnat dva výše uvedené hexadecimální výpisy, abyste zjistili rozdíl mezi soubory „.zero“ a původními textovými soubory. Jako nápovědu vám mohu říci, že nový řádek je zakódován jako 0a byte.

Doufejme, že jste si našli čas potřebný k nalezení znaku NUL ve vstupních souborech „.zero“. Každopádně nyní máme verzi vstupních souborů ukončenou nulou, takže můžeme použít -z možnost paste příkaz pro zpracování těchto dat, přičemž na výstupu se vytvoří také výsledek s nulou:

# 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

Protože mé vstupní soubory neobsahují v datech vložené nové řádky, -z možnost je zde jen omezeně užitečná. Ale na základě výše uvedených vysvětlení vás nechám pokusit se pochopit, proč následující příklad funguje „podle očekávání“. Abyste plně pochopili, že si pravděpodobně budete muset stáhnout ukázkové soubory a prozkoumat je na úrovni bajtů pomocí hexdump utility, jak jsme to udělali výše:

# 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

Co je víc?

paste příkaz vytváří pouze textový výstup s oddělovači. Ale jak je znázorněno na konci úvodního videa, pokud váš systém podporuje column BSD můžete jej použít k získání pěkně formátovaných tabulek převodem paste výstup příkazu do formátu textu s pevnou šířkou. Ale to bude předmětem připravovaného článku. Takže zůstaňte naladěni a jako vždy nezapomeňte tento článek sdílet na svých oblíbených webech a sociálních sítích!


Linux
  1. Linux Cat Command:Použití a příklady

  2. Příkaz Linux AWK – Příklady syntaxe použití Linuxu a Unixu

  3. Příkaz mv v Linuxu:7 základních příkladů

  1. Příkaz Linux Sed:Použití a příklady

  2. Linuxové příkazy:jobs, bg a fg

  3. 4 Základní a praktické použití příkazu Cut v systému Linux

  1. 5 Praktické příklady příkazu „cd“ v Linuxu

  2. 16 Praktické příklady příkazu Traceroute v Linuxu

  3. Linux df příkaz