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.