Crie um arquivo de pares de pares

4

Digamos que eu tenha o seguinte arquivo:

A 1
B 2
CC 33

Eu quero criar um arquivo contendo todas as combinações de dois do arquivo anterior, assim:

AA 11
AB 12
ACC 133
BA 21
BB 22
BCC 233
CCA 331
CCB 332
CCCC 3333

Isso pode ser feito com o bash, para um arquivo arbitrário? Cada entrada pode consistir em quaisquer caracteres, exceto para nova linha e espaço. Os caracteres UTF-8 serão incluídos em algumas entradas.

Eu não me importo com a ordem.

    
por Alice Ryhl 16.05.2015 / 14:22

3 respostas

3

Você pode tentar isso com o arquivo de leitura awk duas vezes:

awk 'NR == FNR { m[$1] = $2; next; } { for (i in m) { print $1 i, $2 m[i]; } }' file file
    
por 16.05.2015 / 14:51
5

Você pode fazer isso totalmente dentro do shell:

while read -r f1 f2
do
    while read -r f3 f4
    do
        printf "%s %s\n" "$f1$f3" "$f2$f4"
    done < your_file
done < your_file

Você diz: "Cada entrada pode consistir em quaisquer caracteres, exceto para nova linha e espaço." Se você realmente quer dizer que as entradas podem conter caracteres de tabulação, diga IFS=" " read em vez de read (ambas as vezes).

A "boa impressão":

Um comando como read f1 f2 lerá a primeira “palavra” na linha de entrada na variável f1 e o resto da linha em f2 . Por exemplo, a entrada The quick brown fox resultaria em f1="The" e f2="quick brown fox" . Se você tem certeza de que seu arquivo nunca terá três (ou mais) colunas (isto é, nunca tenha mais do que duas palavras em qualquer linha), então não há nada para se preocupar. Se você está feliz com tudo o que não faz parte da primeira palavra sendo tratado como parte da segunda palavra, então o código acima deve estar OK.

Mas, se você quiser que The quick brown fox seja tratado como f1="The" e f2="quick" , com brown fox sendo descartado (ignorado), em seguida, adicione uma terceira variável a cada um dos comandos read . Por exemplo, f1 f2 se tornaria f1 f2 x ; isso resultará em f1="The" , f2="quick" e x="brown fox" . Simplesmente por não usar $x , descartamos a entrada após a segunda palavra. O segundo read também pode ser alterado para read -r f3 f4 x - já que não estamos usando $x , não importa se sobrescrevemos. Se você preferir usar uma variável descartável diferente, por exemplo, … f3 f4 y - tudo bem também.

O comando read , por padrão, trata o caractere barra invertida ( \ ) especialmente. Basicamente, barra invertida seguida por qualquer outro caractere mesclar em uma versão especial do segundo caractere. Assim, \C\C seria lido como CC . Mas mais importante, barra invertida seguida de espaço não é tratada como um separador de palavras, e barra invertida seguida de nova linha (ou seja, uma barra invertida no final de uma linha) não é tratado como um separador de linha / terminador. Quando invocamos read com a opção -r , que pára e barra invertida se torna um caractere comum. Aqui estão alguns exemplos práticos das diferenças:

            Without -r (default)                  __ With -r __
_Input_         f1        f2                      f1         f2
A\B\C          AB\C                              A\B\C
D\ E F          D E       F                       D\         E F    (or f2="E" and x="F")
G\          (this doesn’t count as a line)        G\
H               GH                                H

Então, adicionei -r flags à minha primeira versão da minha resposta. Se você quiser manipular D\ E como uma única palavra, não use -r .

    
por 16.05.2015 / 16:23
0

Para preservar o pedido e evitar a leitura do arquivo duas vezes, eu faria:

awk '{f1[NR] = $1; f2[NR] = $2}
     END {
       for (i = 1; i <= NR; i++)
        for (j = 1; j <= NR; j++)
          print f1[i]f1[j], f2[i]f2[j]
     }' file

Agora, isso armazena todo o conteúdo do arquivo na memória antes de processá-lo (como na @ abordagem do taliezin ). Se você preferir não fazer isso, precisará ler o arquivo quantas vezes ele tiver linhas, como na @ abordagem do G-Man . Mas usar awk em vez de sh / bash ( que não foi projetado para isso ) seria muito mais eficiente:

 awk '{f1=$1; f2=$2
       while ((getline < "file") > 0) print f1$1, f2$2
       close("file")}' file
    
por 13.09.2016 / 13:29