Um comando de colagem melhor

10

Eu tenho os dois arquivos a seguir (eu preenchi as linhas com pontos, então cada linha em um arquivo tem a mesma largura e fiz o arquivo 1 todo em maiúsculas para torná-lo mais claro).

contents of file1:

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

contents of file2

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

Observe que o arquivo2 é maior que o arquivo1.

Quando eu executo este comando:

paste file1 file2

Eu recebo esta saída

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

O que posso fazer para que a saída seja a seguinte?

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

Eu tentei

paste file1 file2 | column -t

mas isso é feito:

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

não tão feio quanto a saída original, mas errado na coluna de qualquer maneira.

    
por Tulains Córdova 05.11.2013 / 15:09

6 respostas

16

Supondo que você não tenha nenhum caractere de guia em seus arquivos,

paste file1 file2 | expand -t 13

com o arg para -t escolhido adequadamente para cobrir a largura de linha máxima desejada no arquivo1.

O OP adicionou uma solução mais flexível:

Eu fiz isso para funcionar sem o número mágico 13:

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

Não é fácil digitar, mas pode ser usado em um script.

    
por 05.11.2013 / 16:02
4

Eu achei que o awk poderia fazer isso muito bem, então eu pesquisei "awk reading input from two files" e encontrei um artigo sobre stackoverflow para usar como ponto de partida.

A primeira é a versão condensada, depois totalmente comentada abaixo. Isso levou mais do que alguns minutos para se exercitar. Eu ficaria feliz de alguns refinamentos de pessoas mais inteligentes.

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

E aqui está a versão totalmente documentada dos itens acima.

# 2013-11-05 [email protected]
# 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]:""
  }
}
    
por 06.11.2013 / 01:01
2

Não é uma solução muito boa, mas consegui fazer isso usando

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

onde TAB é substituído pelo caractere de tabulação.

    
por 05.11.2013 / 15:21
2

No Debian e derivados, column tem uma opção -n nomerge que permite que a coluna faça a coisa certa com campos vazios. Internamente, column usa a função wcstok(wcs, delim, ptr) , que divide uma cadeia de caracteres larga em tokens delimitados pelos caracteres largos no argumento delim .

wcstok começa pulando caracteres largos em delim , antes de reconhecer o token. A opção -n usa um algoritmo que não pula os caracteres largos iniciais em delim .

Infelizmente, isso não é muito portável: -n é específico do Debian, e column não está no POSIX, aparentemente é uma coisa BSD.

    
por 06.11.2013 / 18:06
2

Retirando os pontos que você usou para preenchimento:

arquivo1:

ETIAM
SED
MAECENAS
DONEC
SUSPENDISSE

arquivo2:

Lorem
Proin
Nunc
Quisque
Aenean
Nam
Vivamus
Curabitur
Nullam

Tente isto:

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

E você receberá:

ETIAM         Lorem
SED           Proin
MAECENAS      Nunc
DONEC         Quisque
SUSPENDISSE   Aenean
              Nam
              Vivamus
              Curabitur
              Nullam
    
por 15.02.2017 / 21:47
1

Uma solução awk que deve ser razoavelmente portátil e deve funcionar para um número arbitrário de arquivos de entrada:

# 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
    }
}
    
por 06.11.2013 / 20:49