Não tenho certeza se isso é melhor do que fazê-lo na memória, mas com um sed
que r
preenche seu infile para cada linha em seu infile e outro no outro lado de um tubo alternando o H
espaço antigo com as 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'
RESULTADO
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 um pouco 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 a |pipe
. No outro lado do canal, essa entrada é mesclada com o próprio arquivo quantas vezes houver linhas no arquivo.
O case
material é apenas para portabilidade - yash
e zsh
tanto um elemento de adicionar à divisão, enquanto mksh
e posh
tanto um perder. ksh
, dash
, busybox
, E bash
tudo dividido para exatamente quantos campos existem zeros como impressa pelo printf
. Conforme escrito, o acima apresenta os mesmos resultados para cada uma das conchas acima mencionadas na minha máquina.
Se o arquivo for muito longo, poderá haver $ARGMAX
problemas com muitos argumentos; nesse caso, você precisará introduzir xargs
ou similar também.
Dada a mesma entrada que usei antes da saída é idêntica. Mas se eu fosse maior ...
seq 10 10 10000 | nl -s, >/tmp/tmp
Isso gera um arquivo quase idêntico ao que eu usei antes (sans 'Row') - mas com 1000 linhas. Você pode ver por si mesmo o quão 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 ligeira variação no desempenho entre os shells - bash
é invariavelmente o mais lento - mas como o único trabalho que eles fazem é gerar a string arg (1000 cópias filename -
), o efeito é mínimo. A diferença de desempenho entre zsh
- como acima - e bash
é centésimo de 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 suave para o seu primeiro argumento /tmp
com um nome semi-aleatório, para que não fique preso a nomes de arquivos estranhos. Isso é importante porque cat
os argumentos são alimentados através de um cano via xargs
. cat
A saída do arquivo é salva <&3
enquanto sed
p
as linhas do primeiro argumento são copiadas 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 -
novamente para sua entrada padrão e o nome do link /dev/fd/3
.
Esse último - o /dev/fd/[num]
link - deve funcionar em qualquer sistema Linux e muito mais, mas se não criar um pipe nomeado mkfifo
e usá-lo, também funcionará.
A última coisa que ele faz é rm
o link direto criado antes de sair.
Esta versão é realmente mais rápida ainda no meu sistema. Eu acho que é porque, apesar de executar mais aplicativos, ele começa a entregar seus argumentos imediatamente - enquanto antes os empilhava 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