Un comando incolla migliore


11

Ho i seguenti due file (ho riempito le linee di punti in modo che ogni linea in un file abbia la stessa larghezza e ho reso il file1 tutto maiuscolo per renderlo più chiaro).

contents of file1:

ETIAM......
SED........
MAECENAS...
DONEC......
SUSPENDISSE

contents of file2

Lorem....
Proin....
Nunc.....
Quisque..
Aenean...
Nam......
Vivamus..
Curabitur
Nullam...

Si noti che file2 è più lungo di file1.

Quando eseguo questo comando:

paste file1 file2

Ottengo questo risultato

ETIAM...... Lorem....
SED........ Proin....
MAECENAS... Nunc.....
DONEC...... Quisque..
SUSPENDISSE Aenean...
    Nam......
    Vivamus..
    Curabitur
    Nullam...

Cosa posso fare affinché l'output sia il seguente?

ETIAM...... Lorem....
SED........ Proin....
MAECENAS... Nunc.....
DONEC...... Quisque..
SUSPENDISSE Aenean...
            Nam......
            Vivamus..
            Curabitur
            Nullam...

Provai

paste file1 file2 | column -t

ma lo fa:

ETIAM......  Lorem....
SED........  Proin....
MAECENAS...  Nunc.....
DONEC......  Quisque..
SUSPENDISSE  Aenean...
Nam......
Vivamus..
Curabitur
Nullam...

non brutto come l'output originale ma comunque sbagliato nella colonna.


2
pastesta usando le schede davanti alle righe del secondo file. Potrebbe essere necessario utilizzare un postprocessore per allineare le colonne in modo appropriato.
Unxnut

3
paste file1 file2 | column -tn?
ninjalj,

file1 ha sempre colonne di dimensioni fisse?
RSFalcon7,

@ RSFalcon7 Sì, lo fa.
Tulains Córdova,

Risposte:


17

Supponendo che non ci siano caratteri di tabulazione nei tuoi file,

paste file1 file2 | expand -t 13

con l'arg da -tscegliere opportunamente per coprire la larghezza massima della linea desiderata nel file1.

OP ha aggiunto una soluzione più flessibile:

L'ho fatto in modo che funzioni senza il numero magico 13:

paste file1 file2 | expand -t $(( $(wc -L <file1) + 2 ))

Non è facile da scrivere ma può essere utilizzato in uno script.


simpatico! Non sapevo di espandere prima di leggere la tua risposta :)
TabeaKischka il

4

Ho pensato che Awk potesse farlo bene, quindi ho cercato su Google "input di lettura awk da due file" e ho trovato un articolo su StackOverflow da usare come punto di partenza.

La prima è la versione ridotta, quindi completamente commentata di seguito. L'allenamento ha richiesto più di qualche minuto. Sarei felice di alcuni perfezionamenti da parte di persone più intelligenti.

awk '{if(length($0)>max)max=length($0)}
FNR==NR{s1[FNR]=$0;next}{s2[FNR]=$0}
END { format = "%-" max "s\t%-" max "s\n";
  numlines=(NR-FNR)>FNR?NR-FNR:FNR;
  for (i=1; i<=numlines; i++) { printf format, s1[i]?s1[i]:"", s2[i]?s2[i]:"" }
}' file1 file2

Ed ecco la versione completamente documentata di quanto sopra.

# 2013-11-05 mike@diehn.net
# Invoke thus:
#   awk -f this_file file1 file2
# The result is what you asked for and the columns will be
# determined by input file order.
#----------------------------------------------------------
# No matter which file we're reading,
# keep track of max line length for use
# in the printf format.
#
{ if ( length($0) > max ) max=length($0) }

# FNR is record number in current file
# NR is record number over all
# while they are equal, we're reading the first file
#   and we load the strings into array "s1"
#   and then go to the "next" line in the file we're reading.
FNR==NR { s1[FNR]=$0; next }

# and when they aren't, we're reading the
#   second file and we put the strings into
#   array s2
{s2[FNR]=$0}

# At the end, after all lines from both files have
# been read,
END {
  # use the max line length to create a printf format
  # the right widths
  format = "%-" max "s\t%-" max "s\n"
  # and figure the number of array elements we need
  # to cycle through in a for loop.
  numlines=(NR-FNR)>FNR?NR-FNR:FNR;
  for (i=1; i<=numlines; i++) {
     printf format, s1[i]?s1[i]:"", s2[i]?s2[i]:""
  }
}

1
+1 questa è l'unica risposta che funziona con input arbitrari (cioè con righe che possono contenere schede). Non penso che questo potrebbe essere significativamente perfezionato / migliorato.
don_crissti,

2

Non è un'ottima soluzione ma sono stato in grado di farlo utilizzando

paste file1 file2 | sed 's/^TAB/&&/'

dove TAB viene sostituito con il carattere di tabulazione.


Qual è il ruolo del &&comando sed?
coffeMug

1
Un singolo &inserisce ciò che viene cercato (una scheda in questo caso). Questo comando sostituisce semplicemente la scheda all'inizio con due schede.
Unxnut

Ho dovuto cambiare TABper \tfarlo funzionare in zsh su Ubuntu debian. E funziona solo se file1 ha meno di 15 caratteri
rubo77

2

Su Debian e derivati, columnha un'opzione -n nomerge che consente a column di fare la cosa giusta con campi vuoti. Internamente, columnutilizza la wcstok(wcs, delim, ptr)funzione, che divide una stringa di caratteri ampia in token delimitati dai caratteri ampi delimnell'argomento.

wcstokinizia saltando caratteri ampi delimprima di riconoscere il token. L' -nopzione utilizza un algoritmo che non salta i caratteri wide iniziali delim.

Sfortunatamente, questo non è molto portatile: -nè specifico di Debian e columnnon è in POSIX, apparentemente è una cosa di BSD.


2

Eliminare i punti utilizzati per l'imbottitura:

file1:

ETIAM
SED
MAECENAS
DONEC
SUSPENDISSE

file2:

Lorem
Proin
Nunc
Quisque
Aenean
Nam
Vivamus
Curabitur
Nullam

Prova questo:

$ ( echo ".TS"; echo "l l."; paste file1 file2; echo ".TE" ) | tbl | nroff | more

E otterrai:

ETIAM         Lorem
SED           Proin
MAECENAS      Nunc
DONEC         Quisque
SUSPENDISSE   Aenean
              Nam
              Vivamus
              Curabitur
              Nullam

Questo, come le altre soluzioni che utilizzano paste, non riuscirà a stampare l'output corretto se sono presenti righe contenenti schede. +1 per essere diversi però
don_crissti,

+1. Potresti spiegare come funziona la soluzione?
Tulains Córdova,

1

Una awksoluzione che dovrebbe essere abbastanza portatile e dovrebbe funzionare per un numero arbitrario di file di input:

# Invoke thus:
#   awk -F\\t -f this_file file1 file2

# every time we read a new file, FNR goes to 1

FNR==1 {
    curfile++                       # current file
}

# read all files and save all the info we'll need
{
    column[curfile,FNR]=$0          # save current line
    nlines[curfile]++               # number of lines in current file
    if (length > len[curfile])
            len[curfile] = length   # max line length in current file
}

# finally, show the lines from all files side by side, as a table
END {
    # iterate through lines until there are no more lines in any file
    for (line = 1; !end; line++) {
            $0 = _
            end = 1

            # iterate through all files, we cannot use
            #   for (file in nlines) because arrays are unordered
            for (file=1; file <= curfile; file++) {
                    # columnate corresponding line from each file
                    $0 = $0 sprintf("%*s" FS, len[file], column[file,line])
                    # at least some file had a corresponding line
                    if (nlines[file] >= line)
                            end = 0
            }

            # don't print a trailing empty line
            if (!end)
                    print
    }
}

Come lo usi su file1 e file2? Ho chiamato il copione paste-awke ho cercato paste file1 file2|paste-awke ho provato awk paste-awk file1 file2, ma nessuno ha lavorato.
rubo77,

Ricevoawk: Line:1: (FILENAME=file1 FNR=1) Fatal: Division by zero
rubo77 il

@ rubo77: awk -f paste-awk file1 file2dovrebbe funzionare, almeno per GNU awk e mawk.
ninjalj,

Funziona, anche se è leggermente diverso da pastemeno spazio tra le due file. E se il file di input non ha tutte le righe della stessa lunghezza, si otterrà una riga di allineamento a destra
rubo77

@ rubo77: il separatore di campo può essere impostato con-F\\t
ninjalj il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.