it-swarm-eu.dev

kombinujte textové soubory po sloupcích

Mám dva textové soubory. První z nich má obsah:

Languages
Recursively enumerable
Regular

zatímco druhý má obsah:

Minimal automaton
Turing machine
Finite

Chci je zkombinovat do jednoho souboru po sloupcích. Takže jsem se pokusil paste 1 2 a jeho výstup je:

Languages   Minimal automaton
Recursively enumerable  Turing machine
Regular Finite

Chtěl bych však, aby sloupce byly dobře zarovnány, například

Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

Přemýšlel jsem, jestli by bylo možné toho dosáhnout bez manuální manipulace?


Přidané:

Tady je další příklad, kde to Bruceova metoda téměř přibije, kromě nějakého nepatrného vyrovnání, o kterém se divím, proč?

$ cat 1
Chomsky hierarchy
Type-0
—

$ cat 2
Grammars
Unrestricted

$ paste 1 2 | pr -t -e20
Chomsky hierarchy   Grammars
Type-0              Unrestricted
—                    (no common name)
54
Tim

Potřebujete pouze příkaz column a řekněte mu, aby pomocí karet oddělil sloupce

paste file1 file2 | column -s $'\t' -t

K vyřešení diskuse o „prázdné buňce“ potřebujeme pouze -n možnost column:

$ paste <(echo foo; echo; echo barbarbar) <(seq 3) | column -s $'\t' -t
foo        1
2
barbarbar  3

$ paste <(echo foo; echo; echo barbarbar) <(seq 3) | column -s $'\t' -tn
foo        1
           2
barbarbar  3

Moje stránka ve sloupci označuje -n je rozšíření Debian GNU/Linux. Můj systém Fedora nevykazuje problém s prázdnými buňkami: zdá se, že je odvozen z BSD a na stránce man se říká: „Verze 2.23 změnila možnost -s jako nemotorná“

71
glenn jackman

Hledáte šikovný příkaz dandy pr:

paste file1 file2 | pr -t -e24

"-E24" je "rozbalení záložek na 24 mezer". Naštěstí paste vloží mezi sloupce znak na kartě, takže pr jej může rozšířit. Vybral jsem 24 spočtením znaků v „Rekurzivně vyčíslitelných“ a přidáním 2.

12
Bruce Ediger

Aktualizace: Zde mj. Mnohem jednodušší skript (ten na konci otázky) pro tabulkový výstup. Stačí předat název souboru, jako byste to udělali paste... Používá html k vytvoření snímku, takže je vyladitelný. Zachovává více mezer a zarovnání sloupců je zachováno, když narazí na znaky unicode. Způsob, jakým editor nebo prohlížeč vykreslí unicode, je však úplně jiná záležitost ...

┌──────────────────────┬────────────────┬──────────┬────────────────────────────┐
│ Languages            │ Minimal        │ Chomsky  │ Unrestricted               │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│ Recursive            │ Turing machine │ Finite   │     space indented         │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│ Regular              │ Grammars       │          │ ➀ unicode may render oddly │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│ 1 2  3   4    spaces │                │ Symbol-& │ but the column count is ok │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│                      │                │          │ Context                    │
└──────────────────────┴────────────────┴──────────┴────────────────────────────┘

#!/bin/bash
{ echo -e "<html>\n<table border=1 cellpadding=0 cellspacing=0>"
  paste "[email protected]" |sed -re 's#(.*)#\x09\1\x09#' -e 's#\x09# </pre></td>\n<td><pre> #g' -e 's#^ </pre></td>#<tr>#' -e 's#\n<td><pre> $#\n</tr>#'
  echo -e "</table>\n</html>"
} |w3m -dump -T 'text/html'

---

Souhrn nástrojů uvedené v odpovědích (dosud).
Díval jsem se na ně docela zblízka; tady je to, co jsem našel:

paste # Tento nástroj je společný pro všechny dosud předložené odpovědi # Dokáže zpracovat více souborů; proto více sloupců ... Dobře! # Vymezuje každý sloupec tabulátorem ... Dobře. # Jeho výstup není v tabulce.

Všechny níže uvedené nástroje odstraňují tento oddělovač! ... Špatné, pokud potřebujete oddělovač.

column # Odstraňuje oddělovač tabulátorů, takže identifikátor pole je čistě sloupců, které se zdají docela dobře zpracovány .. Neviděl jsem nic strašlivého ... # Kromě toho, že nemáme jedinečný oddělovač, funguje to pokuta!

expand # Má pouze jedno nastavení karty, takže je nepředvídatelné za 2 sloupce # Zarovnání sloupců není přesné při manipulaci s unicode a odstraní oddělovač Tab, takže identifikátor pole je čistě zarovnání sloupců

pr # Má pouze jedno nastavení karty, takže je nepředvídatelné za 2 sloupci. # Zarovnání sloupců není přesné při manipulaci s Unicode a odstraní oddělovač Tab, takže identifikátor pole je čistě zarovnání sloupců

Pro mě, column je to zřejmé nejlepší řešení jako jednovrstvé .. Chcete, aby vaše oddělovače nebo ASCII-art tabluation vašich souborů, číst, jinak .. columns je docela zatraceně dobrý:) ...


Zde je skript, který vezme jakýkoli odkaz na soubory a vytvoří prezentaci ASCII-art v tabulce .. (Mějte na paměti, že unicode se nemusí vykreslit na očekávanou šířku, např. ௵ což je jediný znak. To je zcela odlišné od sloupce čísla jsou špatná, jako je tomu v některých výše uvedených nástrojích.) ... Výstup skriptu, uvedený níže, je ze 4 vstupních souborů s názvem F1 F2 F3 F4 ...

+------------------------+-------------------+-------------------+--------------+
| Languages              | Minimal automaton | Chomsky hierarchy | Grammars     |
| Recursively enumerable | Turing machine    | Type-0            | Unrestricted |
| Regular                | Finite            | —                 |              |
| Alphabet               |                   | Symbol            |              |
|                        |                   |                   | Context      |
+------------------------+-------------------+-------------------+--------------+

#!/bin/bash

# Note: The next line is for testing purposes only!
set F1 F2 F3 F4 # Simulate commandline filename args $1 $2 etc...

p=' '                                # The pad character
# Get line and column stats
cc=${#@}; lmax=                      # Count of columns (== input files)
for c in $(seq 1 $cc) ;do            # Filenames from the commandline 
  F[$c]="${!c}"        
  wc=($(wc -l -L <${F[$c]}))         # File length and width of longest line 
  l[$c]=${wc[0]}                     # File length  (per file)
  L[$c]=${wc[1]}                     # Longest line (per file) 
  ((lmax<${l[$c]})) && lmax=${l[$c]} # Length of longest file
done
# Determine line-count deficits  of shorter files
for c in $(seq 1 $cc) ;do  
  ((${l[$c]}<lmax)) && D[$c]=$((lmax-${l[$c]})) || D[$c]=0 
done
# Build '\n' strings to cater for short-file deficits
for c in $(seq 1 $cc) ;do
  for n in $(seq 1 ${D[$c]}) ;do
    N[$c]=${N[$c]}$'\n'
  done
done
# Build the command to suit the number of input files
source=$(mktemp)
>"$source" echo 'paste \'
for c in $(seq 1 $cc) ;do
    ((${L[$c]}==0)) && e="x" || e=":a -e \"s/^.{0,$((${L[$c]}-1))}$/&$p/;ta\""
    >>"$source" echo '<(sed -re '"$e"' <(cat "${F['$c']}"; echo -n "${N['$c']}")) \'
done
# include the ASCII-art Table framework
>>"$source" echo ' | sed  -e "s/.*/| & |/" -e "s/\t/ | /g" \'   # Add vertical frame lines
>>"$source" echo ' | sed -re "1 {h;s/[^|]/-/g;s/\|/+/g;p;g}" \' # Add top and botom frame lines 
>>"$source" echo '        -e "$ {p;s/[^|]/-/g;s/\|/+/g}"'
>>"$source" echo  
# Run the code
source "$source"
rm     "$source"
exit

Zde je moje původní odpověď (oříznutá trochu na místě výše uvedeného skriptu)

Pomocí wc získáte šířku sloupce a sed na pravou podložku s viditelným znakem . (jen pro tento příklad) ... a poté paste pro spojení dvou sloupců s Tab char ...

paste <(sed -re :a -e 's/^.{1,'"$(($(wc -L <F1)-1))"'}$/&./;ta' F1) F2

# output (No trailing whitespace)
Languages.............  Minimal automaton
Recursively enumerable  Turing machine
Regular...............  Finite

Pokud chcete vyplnit pravý sloupec:

paste <( sed -re :a -e 's/^.{1,'"$(($(wc -L <F1)-1))"'}$/&./;ta' F1 ) \
      <( sed -re :a -e 's/^.{1,'"$(($(wc -L <F2)-1))"'}$/&./;ta' F2 )  

# output (With trailing whitespace)
Languages.............  Minimal automaton
Recursively enumerable  Turing machine...
Regular...............  Finite...........
9
Peter.O

Už jste skoro tam. paste vloží mezi jednotlivé sloupce znak na kartě, takže vše, co musíte udělat, je rozšířit karty. (Předpokládám, že vaše soubory neobsahují karty.) Musíte určit šířku levého sloupce. U (dostatečně nedávných) GNU utilit, wc -L) Se zobrazí délka nejdelší linky. V jiných systémech udělejte první průchod s awk. +1 Je množství volného místa mezi sloupci.

paste left.txt right.txt | expand -t $(($(wc -L <left.txt) + 1))
paste left.txt right.txt | expand -t $(awk 'n<length {n=length} END {print n+1}')

Pokud máte obslužný program sloupce BSD, můžete jej použít k určení šířky sloupce a rozbalení karet najednou. ( Je znak doslovné karty; pod bash/ksh/zsh můžete místo toho použít $'\t' A v každém Shell můžete použít "$(printf '\t')".)

paste left.txt right.txt | column -s '␉' -t

Jedná se o více kroků, takže není optimální, ale jde to.

1) Najděte délku nejdelší linie v file1.txt.

while read line
do
echo ${#line}
done < file1.txt | sort -n | tail -1

V příkladu je nejdelší řádek 22.

2) Použijte awk k vložení file1.txt, vyplňovat každý řádek méně než 22 znaků až 22 pomocí příkazu printf.

awk 'FS="---" {printf "%-22s\n", $1}' < file1.txt > file1-pad.txt

Poznámka: Pro FS použijte řetězec, který neexistuje v file1.txt.

3) Použijte pastu jako předtím.

$ paste file1-pad.txt file2.txt
Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

Pokud je to něco, co často děláte, lze to snadno změnit na skript.

4
bahamat

Nemohu se vyjádřit k odpovědi glenn jackman, takže přidávám toto, abych se zabýval otázkou prázdných buněk, které si všiml Peter.O. Přidání nulového znaku před každou kartu eliminuje běhy oddělovačů, které jsou považovány za jednu přestávku, a řeší problém. (Původně jsem použil mezery, ale použití nulové char eliminuje mezeru mezi sloupci.)

paste file1 file2 | sed 's/\t/\0\t/g' | column -s $'\t' -t

Pokud nulový znak způsobuje problémy z různých důvodů, zkuste buď:

paste file1 file2 | sed 's/\t/ \t/g' | column -s $'\t' -t

nebo

paste file1 file2 | sed $'s/\t/ \t/g' | column -s $'\t' -t

Zdá se, že jak sed, tak column se liší v implementaci napříč chutěmi a verzemi Unix/Linux, zejména BSD (a Mac OS X) vs. GNU/Linux.

4
techno

Staví na bahamatova odpověď : to lze provést zcela v awk, čtení souborů pouze jednou a nevytváření dočasných souborů. Chcete-li vyřešit problém, jak je uvedeno, proveďte

awk '
        NR==FNR { if (length > max_length) max_length = length
                  max_FNR = FNR
                  save[FNR] = $0
                  next
                }
                { printf "%-*s", max_length+2, save[FNR]
                  print
                }
        END     { if (FNR < max_FNR) {
                        for (i=FNR+1; i <= max_FNR; i++) print save[i]
                  }
                }
    '   file1 file2

Stejně jako u mnoha awk skriptů tohoto ilku, výše uvedené první přečte file1, uložení všech dat do pole save a současná výpočet maximální délky řádku. Pak to přečte file2 a vytiskne uložené (file1) data vedle sebe s aktuálním (file2) data. Nakonec, pokud file1 je delší než file2 (má více řádků), vytiskneme několik posledních řádků file1 (ty, pro které ve druhém sloupci není odpovídající řádek).

Pokud jde o formát printf:

  • "%-nns" vytiskne řetězec vlevo zarovnán do pole nn znaků široký.
  • "%-*s", nn dělá totéž - * řekne tomu, aby převzal šířku pole od dalšího parametru.
  • Používáním maxlength+2 pro nn, dostaneme dva sloupce mezi sloupci. Zjevně +2 lze upravit.

Výše uvedený skript funguje pouze pro dva soubory. Může být triviálně upraven tak, aby zpracovával tři soubory nebo zpracovával čtyři soubory atd., Ale to by bylo únavné a zůstalo by to cvičení. Ukázalo se však, že není obtížné jej upravovat libovolné číslo z soubory:

awk '
        FNR==1  { file_num++ }
                { if (length > max_length[file_num]) max_length[file_num] = length
                  max_FNR[file_num] = FNR
                  save[file_num,FNR] = $0
                }
        END     { for (j=1; j<=file_num; j++) {
                        if (max_FNR[j] > global_max_FNR) global_max_FNR = max_FNR[j]
                  }
                  for (i=1; i<=global_max_FNR; i++) {
                        for (j=1; j<file_num; j++) printf "%-*s", max_length[j]+2, save[j,i]
                        print save[file_num,i]
                  }
                }
    '   file*

To je velmi podobné mému prvnímu skriptu

  • Otočí se max_length do pole.
  • Otočí se max_FNR do pole.
  • Z proměnné save se stává dvojrozměrné pole.
  • Přečte všechny soubory, uloží vše obsah. Pak vypíše vše výstup z bloku END.