it-swarm.it

combina file di testo in base alla colonna

Ho due file di testo. Il primo ha contenuto:

Languages
Recursively enumerable
Regular

mentre il secondo ha contenuto:

Minimal automaton
Turing machine
Finite

Voglio combinarli in un file per quanto riguarda le colonne. Quindi ho provato paste 1 2 e il suo output è:

Languages   Minimal automaton
Recursively enumerable  Turing machine
Regular Finite

Tuttavia vorrei che le colonne fossero allineate bene come

Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

Mi chiedevo se sarebbe stato possibile farlo senza una gestione manuale?


Inserito il:

Ecco un altro esempio, in cui il metodo Bruce quasi lo inchioda, tranne qualche leggero disallineamento sul quale mi chiedo perché?

$ 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

Hai solo bisogno del comando column e digli di usare le schede per separare le colonne

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

Per affrontare la controversia sulla "cella vuota", abbiamo solo bisogno del -n opzione su 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

La pagina man della mia colonna indica -n è "un'estensione Debian GNU/Linux". Il mio sistema Fedora non presenta il problema delle celle vuote: sembra derivato da BSD e la pagina man dice "La versione 2.23 ha cambiato l'opzione -s in modo da non essere avida"

71
glenn jackman

Stai cercando il comodo comando dandy pr:

paste file1 file2 | pr -t -e24

"-E24" è "espandi le tabulazioni a 24 spazi". Fortunatamente, paste inserisce un carattere di tabulazione tra le colonne, quindi pr può espanderlo. Ho scelto 24 contando i caratteri in "Elenco ricorsivo" e aggiungendo 2.

12
Bruce Ediger

Aggiornamento: qui è uno script molto più semplice (quello alla fine della domanda) per l'output tabulato. Basta passargli il nome del file come si farebbe con paste... Usa html per creare il frame, quindi è modificabile. Conserva più spazi e l'allineamento della colonna viene preservato quando incontra caratteri unicode. Tuttavia, il modo in cui l'editor o il visualizzatore esegue il rendering dell'unicode è un'altra questione del tutto ...

┌──────────────────────┬────────────────┬──────────┬────────────────────────────┐
│ 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'

---

na sinossi degli strumenti presentato nelle risposte (finora).
Li ho guardati da vicino; ecco cosa ho trovato:

paste # Questo strumento è comune a tutte le risposte finora presentate # Può gestire più file; quindi più colonne ... Bene! # Delimita ogni colonna con una scheda ... Buono. # Il suo output non è tabulato.

Tutti gli strumenti sottostanti rimuovono questo delimitatore! ... Male se hai bisogno di un delimitatore.

column # Rimuove il delimitatore Tab, quindi l'identificazione del campo è puramente per colonne che sembra gestire abbastanza bene .. Non ho notato nulla di storto ... # Oltre a non avere un delimitatore univoco, funziona bene!

expand # ha solo una singola impostazione della scheda, quindi è imprevedibile oltre 2 colonne # L'allineamento delle colonne non è accurato durante la gestione dell'unicode e rimuove il delimitatore Tab, quindi l'identificazione del campo è puramente allineata alle colonne

pr # ha solo una singola scheda, quindi è imprevedibile oltre 2 colonne. # L'allineamento delle colonne non è preciso durante la gestione dell'unicode e rimuove il delimitatore Tab, quindi l'identificazione del campo è puramente mediante l'allineamento delle colonne

Per me, column è il miglior ovvio miglior solitario come one-liner .. Se vuoi il delimitatore o una tabluation in formato ASCII dei tuoi file, continua a leggere, altrimenti .. columns è dannatamente buono:) ...


Ecco uno script che prende qualsiasi numero di file e crea una presentazione tabulata in stile ASCII .. (Ricorda che unicode potrebbe non renderizzare alla larghezza prevista, ad es. ௵ che è un singolo carattere. Questo è abbastanza diverso dalla colonna i numeri sono errati, come nel caso di alcune delle utilità menzionate sopra.) ... L'output dello script, mostrato di seguito, proviene da 4 file di input, denominati 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

Ecco la mia risposta originale (ritagliata un po 'al posto della sceneggiatura sopra)

Usando wc per ottenere la larghezza della colonna e sed sul pad destro con un carattere visibile. (Solo per questo esempio) ... e quindi paste per unire le due colonne con un 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

Se vuoi riempire la colonna di destra:

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

Ci sei quasi. paste inserisce un carattere di tabulazione tra ogni colonna, quindi tutto ciò che devi fare è espandere le schede. (Suppongo che i tuoi file non contengano schede.) Devi determinare la larghezza della colonna di sinistra. Con (abbastanza recente) GNU utility, wc -L Mostra la lunghezza della linea più lunga. Su altri sistemi, fai un primo passaggio con awk. Il +1 È la quantità di spazio vuoto desiderata tra le colonne.

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}')

Se si dispone dell'utilità di colonna BSD, è possibile utilizzarla per determinare la larghezza della colonna ed espandere le schede in una volta sola. ( È un carattere di tabulazione letterale; sotto bash/ksh/zsh puoi usare invece $'\t', E in qualsiasi Shell puoi usare "$(printf '\t')".)

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

Questo è multi-step, quindi non è ottimale, ma qui va.

1) Trova la lunghezza della linea più lunga in file1.txt.

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

Con il tuo esempio, la linea più lunga è 22.

2) Usa awk per pad file1.txt, riempiendo ciascuna riga di meno di 22 caratteri fino a 22 con l'istruzione printf.

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

Nota: per FS, utilizzare una stringa che non esiste in file1.txt.

3) Usa la pasta come facevi prima.

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

Se questo è qualcosa che fai spesso, questo può essere facilmente trasformato in uno script.

4
bahamat

Non sono in grado di commentare la risposta di Glenn Jackman, quindi sto aggiungendo questo per affrontare il problema delle celle vuote che Peter.O ha notato. L'aggiunta di un carattere null prima di ogni scheda elimina le esecuzioni di delimitatori che vengono trattate come un'unica interruzione e risolve il problema. (Inizialmente ho usato gli spazi, ma l'uso del carattere null elimina lo spazio aggiuntivo tra le colonne.)

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

Se il carattere null causa problemi per vari motivi, provare uno di questi:

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

o

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

Sia sed che column sembrano variare nell'implementazione tra versioni e versioni di Unix/Linux, in particolare BSD (e Mac OS X) rispetto a GNU/Linux.

4
techno

Basandosi su la risposta di bahamat : questo può essere fatto interamente in awk, leggendo i file una sola volta e non creando alcun file temporaneo. Per risolvere il problema come indicato, fare

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

Come con molti script awk di questo ilk, la prima sopra dice file1, salvando tutti i dati nell'array save e calcolando contemporaneamente la lunghezza massima della linea. Quindi legge file2 e stampa il salvato (file1) dati affiancati all'attuale (file2) dati. Infine, se file1 è più lungo di file2 (ha più righe), stampiamo le ultime righe di file1 (quelli per i quali non esiste una riga corrispondente nella seconda colonna).

Per quanto riguarda il formato printf:

  • "%-nns" stampa una stringa giustificata a sinistra in un campo nn caratteri.
  • "%-*s", nn fa la stessa cosa: il * gli dice di prendere la larghezza del campo dal prossimo parametro.
  • Usando maxlength+2 for nn, otteniamo due spazi tra le colonne. Ovviamente il +2 può essere regolato.

Lo script sopra funziona solo per due file. Può essere banalmente modificato per gestire tre file o per gestire quattro file, ecc., Ma sarebbe noioso e rimarrebbe un esercizio. Tuttavia, risulta difficile non modificarlo per gestirlo qualsiasi numero of file:

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*

Questo è molto simile al mio primo copione, tranne

  • Gira max_length in un array.
  • Gira max_FNR in un array.
  • Trasforma save in un array bidimensionale.
  • Legge tutto i file, salvando tutto il contenuto. Quindi scrive tutto l'output dal blocco END.