Não tenho certeza se isso é melhor do que fazer isso na memória, mas com sed
que r
extrai seu arquivo para cada linha em seu arquivo e outro no outro lado de um canal alternando H
espaço antigo com linhas de entrada ...
cat <<\IN >/tmp/tmp
Row1,10
Row2,20
Row3,30
Row4,40
IN
</tmp/tmp sed -e 'i\
' -e 'r /tmp/tmp' |
sed -n '/./!n;h;N;/\n$/D;G;s/\n/ /;P;D'
OUTPUT
Row1,10 Row1,10
Row1,10 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row2,20 Row1,10
Row2,20 Row2,20
Row2,20 Row3,30
Row2,20 Row4,40
Row3,30 Row1,10
Row3,30 Row2,20
Row3,30 Row3,30
Row3,30 Row4,40
Row4,40 Row1,10
Row4,40 Row2,20
Row4,40 Row3,30
Row4,40 Row4,40
Eu fiz isso de outra maneira. Ele armazena alguns na memória - armazena uma string como:
"$1" -
... para cada linha no arquivo.
pairs(){ [ -e "$1" ] || return
set -- "$1" "$(IFS=0 n=
case "${0%sh*}" in (ya|*s) n=-1;; (mk|po) n=+1;;esac
printf '"$1" - %s' $(printf "%.$(($(wc -l <"$1")$n))d" 0))"
eval "cat -- $2 </dev/null | paste -d ' \n' -- $2"
}
É muito rápido. É cat
o arquivo quantas vezes houver linhas no arquivo para um |pipe
. Do outro lado do canal, a entrada é mesclada com o arquivo em si, quantas vezes houver linhas no arquivo.
O material case
é apenas para portabilidade - yash
e zsh
adicionam um elemento à divisão, enquanto mksh
e posh
perdem um. ksh
, dash
, busybox
e bash
são divididos em exatamente o mesmo número de campos que zeros impressos em printf
. Como foi escrito acima, obtemos os mesmos resultados para cada um dos shells mencionados acima em minha máquina.
Se o arquivo for muito longo, pode haver $ARGMAX
problemas com muitos argumentos, caso em que você precisaria introduzir xargs
ou similar também.
Dada a mesma entrada que eu usei antes da saída ser idêntica. Mas, se eu fosse aumentar ...
seq 10 10 10000 | nl -s, >/tmp/tmp
Isso gera um arquivo quase idêntico ao que eu usei antes (sans 'Row') - mas em 1000 linhas. Você pode ver por si mesmo como é rápido:
time pairs /tmp/tmp |wc -l
1000000
pairs /tmp/tmp 0.20s user 0.07s system 110% cpu 0.239 total
wc -l 0.05s user 0.03s system 32% cpu 0.238 total
Em 1000 linhas há uma pequena variação no desempenho entre shells - bash
é invariavelmente a mais lenta - mas porque o único trabalho que eles fazem é gerar a string arg (1000 cópias de filename -
) o efeito é mínimo. A diferença de desempenho entre zsh
- como acima - e bash
é 100º segundo aqui.
Aqui está outra versão que deve funcionar para um arquivo de qualquer tamanho:
pairs2()( [ -e "$1" ] || exit
rpt() until [ "$((n+=1))" -gt "$1" ]
do printf %s\n "$2"
done
[ -n "${1##*/*}" ] || cd -P -- "${1%/*}" || exit
: & set -- "$1" "/tmp/pairs$!.ln" "$(wc -l <"$1")"
ln -s "$PWD/${1##*/}" "$2" || exit
n=0 rpt "$3" "$2" | xargs cat | { exec 3<&0
n=0 rpt "$3" p | sed -nf - "$2" | paste - /dev/fd/3
}; rm "$2"
)
Ele cria um link para seu primeiro argumento em /tmp
com um nome semi-aleatório para que ele não seja colocado em nomes de arquivos estranhos. Isso é importante porque os argumentos de cat
são enviados a ele por meio de um canal via xargs
. A saída de cat
é salva em <&3
, enquanto sed
p
solicita cada linha no primeiro argumento, quantas vezes houver linhas nesse arquivo - e seu script também é alimentado por meio de um pipe. Novamente paste
mescla sua entrada, mas desta vez são necessários apenas dois argumentos -
para sua entrada padrão e o nome do link /dev/fd/3
.
Esse último - o link /dev/fd/[num]
- deve funcionar em qualquer sistema Linux e muito mais, mas se ele não criar um canal nomeado com mkfifo
e usar isso, também deverá funcionar.
A última coisa que faz é rm
do link que cria antes de sair.
Esta versão é realmente mais rápida ainda no meu sistema. Eu acho que é porque, embora ele execute mais aplicativos, ele começa a entregá-los seus argumentos imediatamente - enquanto antes eles empilhavam todos eles primeiro.
time pairs2 /tmp/tmp | wc -l
1000000
pairs2 /tmp/tmp 0.30s user 0.09s system 178% cpu 0.218 total
wc -l 0.03s user 0.02s system 26% cpu 0.218 total