Amostra de um elemento de cada linha do arquivo até k

2

Eu sou novo no UNIX e estou tentando aprender o processamento de texto de nível básico no UNIX, então talvez essa questão possa parecer muito básica, mas eu realmente aprecio qualquer orientação.

Eu tenho um arquivo de texto como abaixo

A 1
B 2
C 9
D 1
A 5
B 3
C 4
A 6
C 7
A 5
C 1

para o qual eu consegui juntar um comando com alguma ajuda como abaixo

cat | awk 'BEGIN{OFS=FS=" "}!(($1,$2)in c){c[$1,$2]=1;r[$1]=r[$1] OFS $2}
END{for(i in r){print i r[i]}}'

dentro de um script de shell para chegar ao formato abaixo:

A 1 5 6 5
B 2 3
C 9 4 7 1
D 1

Meu shell script também recebe um argumento k (= 7 digamos)

Neste caso, meu script deve amostrar 1 elemento de cada caixa (A, B, C, D) até que tenha um total de 7 elementos amostrados. Isso será como segue: Primeiro escolha 1 de A, depois 2 de B, 9 de C, 1 de D, 5 de A, 3 de B, 4 de C e exiba

A 1 5
B 2 3
C 9 4
D 1

Se em vez disso eu tivesse k = 9 como minha entrada, então minha saída deveria ser

A 1 5 6
B 2 3
C 9 4 7
D 1
    
por Sabrish Gopalakrishnan 10.03.2018 / 04:00

2 respostas

2

Em uma linguagem de alto nível, você usaria uma matriz de arrays, mas o bash não tem isso. Problemas que sugerem estruturas de dados multiníveis, como esta, tendem a ser muito tediosas para serem resolvidas no shell.

Mas como seu objetivo é aprender o processamento de texto Unix, e não o Python, vamos resolvê-lo no shell.

Nesta solução, lemos o arquivo uma vez para obter os cabeçalhos de linha e, novamente, quantas vezes forem necessárias para coletar o número necessário de elementos. Mantemos dois arrays: outrow é uma matriz de linhas de saída com cada linha sendo anexada à medida que vamos; cursor é uma matriz de inteiros que armazena nosso lugar em cada linha.

Observe que esse script fará um loop para sempre se não houver elementos suficientes para atender à solicitação. Resolvendo isso é deixado como um exercício para o leitor.

#!/bin/bash
k=$1
input=input.txt
declare -a outrow
declare -a cursor
K=0
n=0
while read line
do
    outrow[$n]=${line%% *}
    cursor[$n]=1
    (( n++ ))
done < $input

while [[ $K -lt $k ]]
do
    n=0
    while read line
    do
        declare -a col=( $line )
        if [[ ${#col[@]} -gt ${cursor[$n]} ]]
        then
            outrow[$n]+=" ${col[ ${cursor[$n]} ]}"
            (( cursor[$n]++ ))
            (( K++ ))
            [[ $K -lt $k ]] || break
        fi
        (( n++ ))
    done < $input
done

for row in "${outrow[@]}"
do
    echo "$row"
done
    
por 10.03.2018 / 05:54
1

Observação: alterando a variável num , você pode regular a quantidade de elementos.

gawk -v num=5 '
BEGIN {
    PROCINFO["sorted_in"] = "@ind_str_asc"
}
{
    ### 
    # Traverse throught input.txt from first to last line
    # and store all elements in the two-dimensional array - table
    # along the way, maintain the array of counters for each letter
    ###

    # The array of counters for each unique element from the first column.
    # In our case the indexes of array are capital letters (A, B, C, D)
    # and values are the amount of each letter occurrences.
    cnt_arr[$1]++

    # Two dimension array - table
    # it looks like chess board - rows named by letters (A, B, C, D)
    # and columns named by numbers (1, 2, 3, 4, 5... etc).
    # Its cells contains numbers from the second column.
    # For example, if letter A occurrences 5 times in the input.txt
    # then, the table will have the A row with 5 columns 
    table[$1][cnt_arr[$1]] = $2
}
# At this point, all lines from input.txt are processed
# and stored in the table
END {
    # Do needed number of iterations - specified by the num variable
    for(i = 0; i < num; i++) {

        # On each iteration run the inner loop,
        # which iterating through all rows in the table
        for(row_name in table) {

            # Check each cell - if it is non-empty
            # add its value to the result_arr[row_name], separated by OFS.
            # OFS - output field separator, the space by default
            if(table[row_name][i]) {
                result_arr[row_name] = result_arr[row_name] OFS table[row_name][i]
                # and count the number of succesful occurences
                cnt++
            }

            # If count of non-empty cells equals to the num variable
            # or equals to the NR (number of records|lines)
            # print the result_arr and exit
            if(cnt == num || cnt >= NR) {
                for(i in result_arr) {
                    print i result_arr[i]
                }
                exit
            }
        }
    }
}' input.txt

As informações sobre a linha PROCINFO["sorted_in"] = "@ind_str_asc" são aqui .

Entrada

A 1
B 2
C 9
D 1
A 5
B 3
C 9
A 6
C 7
A 5
C 1

Resultado

A 1 5
B 2
C 9
D 1
    
por 10.03.2018 / 17:30