Sobre a aplicação de comandos a grupos de linhas de stdin

3

O pacote Unix datamash suporta a aplicação de várias operações de resumo para grupos de linhas de entrada. Por exemplo 1 , aqui datamash é usado para calcular as somas da coluna 2 para cada valor na coluna 1:

$ cat example.csv
1,10
1,5
2,9
2,11
$ datamash -t, -g 1 sum 2 < example.csv
1,15
2,20

Embora datamash suporte uma ampla gama de funções além de sum (incluindo mean , stddev , median , iqr , min , max , etc.), não é extensível, AFAICT. IOW, datamash não suporta nenhum mecanismo para o usuário fornecer sua própria função de resumo.

Minha pergunta aqui se resume a: como essa aplicação de comandos em grupo pode ser implementada genericamente em zsh 2 ?

Abaixo está uma tentativa de especificar a pergunta com mais precisão. (Espero que esta tentativa de precisão não torne a questão incompreensível.)

Primeiro, suponha que foo represente um comando (possivelmente composto) que emite linhas stdout com a seguinte estrutura:

eu separador carga útil i j

... onde i , o "índice de grupo", é algum inteiro, separador é alguma sequência separadora constante (por exemplo, , ou $'\t' ) e payload i j é um texto arbitrário (incluindo a nova linha de terminação). Além disso, suponha que o índice de grupo i varie de 1 a N , e que as linhas nessa saída sejam classificadas de acordo com o índice do grupo.

Para todo o inteiro 1 & leq; k & N , deixe o " k -ésimo grupo" referir-se ao conteúdo que consiste nos segmentos payload k j de todas as linhas (na saída de foo ) onde o índice do grupo é k .

Em seguida, suponha que bar represente um comando (possivelmente composto) que leia linhas de stdin e emita uma única linha para stdout.

Agora, vamos resultar k denotar a saída da aplicação de bar ao k -th grupo e deixe X<bar> representar uma construção de shell que invoca bar .

Estou basicamente procurando por uma construção X<bar> de modo que o pipeline

foo | X<bar>

emite para stdout linhas do formulário

i separador resultado i

EDITAR:

Supondo que o separador é apenas , , então o seguinte parece fazer o que eu quero

TMPFILE=$( mktemp )
SEPARATOR=,
LASTGROUPID=
foo | (cat; echo) | while IFS= read -r LINE
do
    GROUPID=${LINE%%$SEPARATOR*}
    if [[ $GROUPID != $LASTGROUPID ]]
    then
        if [[ -n $LASTGROUPID ]]
        then
            echo -n "$LASTGROUPID$SEPARATOR"
            cat $TMPFILE | bar
        fi
        LASTGROUPID=$GROUPID
        : > $TMPFILE
    fi
    PAYLOAD=${LINE#*$SEPARATOR}
    echo $PAYLOAD >> $TMPFILE
done
rm $TMPFILE

Basicamente, use $TMPFILE para coletar as linhas no próximo grupo. (Eu prefiro evitar o arquivo temporário, mas não sei como fazer isso.)

Agora, preciso descobrir uma maneira de implementar isso como uma função que pode usar a expressão denotada por bar como argumento e usá-la de maneira robusta na construção acima.

1 Este exemplo é adaptado de um dado na página datamash man.

2 Embora eu esteja interessado principalmente em zsh , também tenho um caso% bash de interesse secundário.

    
por kjo 18.03.2016 / 21:50

2 respostas

2

Não me parece um trabalho para um shell. Eu faria isso em perl / python / ruby ... embora aqui awk possa ser suficiente:

$ cat sum
paste -sd + - | bc
$ sort -t , -k 1,1 input | awk -F, -v cmd=./sum '
   function out() {printf "%s,", l;close(cmd)}
   NR>1 && $1 != l {out()}
   {print $2 | cmd; l=$1}
   END {if (NR) out()}'
1,15
2,20
    
por 19.03.2016 / 00:01
2

Se eu tenho a idéia do que você está procurando: um script análogo aos que produzem a distribuição de um conjunto de amostras, mas com mais opções de acumulação. Eu escrevi um script awk para isso.

link

Não é exatamente o que você quer, mas a sobreposição deve ser significativa. Primeiro - os índices podem não ser apenas inteiros, segundo - o único método de acumulação é o somatório. Mas como é apenas um script, você pode modificá-lo como quiser mais facilmente do que o programa C.

Por fim, esses scripts são aplicáveis somente se o conjunto de dados for pequeno o suficiente, para conjuntos maiores é muito lento! Portanto, um pacote mais especializado é preferível ( R etc.).

P.S. Para adicionar outros acumuladores, substitua += por uma função personalizada (a la "monad").

    
por 19.03.2016 / 00:44