Je ne suis pas sûr que ce soit mieux que de le faire en mémoire, mais avec un sed
qui r
lit son fichier pour chaque ligne de son fichier et un autre de l'autre côté d'un tuyau alternant l' H
ancien espace avec des lignes d'entrée ...
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'
PRODUCTION
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
Je l'ai fait d'une autre manière. Il en stocke certains en mémoire - il stocke une chaîne comme:
"$1" -
... pour chaque ligne du fichier.
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"
}
C'est très rapide. C'est cat
le fichier autant de fois qu'il y a de lignes dans le fichier vers a |pipe
. De l'autre côté du canal, cette entrée est fusionnée avec le fichier lui-même autant de fois qu'il y a de lignes dans le fichier.
Le case
contenu est juste pour la portabilité - yash
et les zsh
deux ajoutent un élément à la division, tandis mksh
que les posh
deux en perdent un. ksh
, dash
, busybox
Et bash
toutes divisées en exactement autant de champs comme il y a des zéros comme imprimé par printf
. Tel qu'écrit, ce qui précède donne les mêmes résultats pour chacun des obus mentionnés ci-dessus sur ma machine.
Si le fichier est très long, il peut y avoir des $ARGMAX
problèmes avec trop d'arguments, auquel cas vous devrez également introduire xargs
ou similaire.
Étant donné la même entrée que j'ai utilisée avant la sortie est identique. Mais si je devais aller plus loin ...
seq 10 10 10000 | nl -s, >/tmp/tmp
Cela génère un fichier presque identique à ce que j'utilisais auparavant (sans 'Row') - mais à 1000 lignes. Vous pouvez voir par vous-même à quelle vitesse il est:
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
À 1000 lignes, il y a une légère variation de performances entre les shells - bash
c'est invariablement la plus lente - mais parce que le seul travail qu'ils font de toute façon est de générer la chaîne d'argument (1000 copies de filename -
) l'effet est minime. La différence de performances entre zsh
- comme ci-dessus - et bash
est de 100e de seconde ici.
Voici une autre version qui devrait fonctionner pour un fichier de n'importe quelle longueur:
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"
)
Il crée un lien logiciel vers son premier argument /tmp
avec un nom semi-aléatoire afin qu'il ne se bloque pas sur des noms de fichiers étranges. C'est important parce que cat
les arguments sont alimentés via un tuyau via xargs
. cat
La sortie de 'est enregistrée dans <&3
tandis sed
p
qu'imprime chaque ligne dans le premier argument autant de fois qu'il y a de lignes dans ce fichier - et son script lui est également envoyé via un tube. paste
Fusionne à nouveau son entrée, mais cette fois, il ne prend à nouveau que deux arguments -
pour son entrée standard et le nom du lien /dev/fd/3
.
Ce dernier - le /dev/fd/[num]
lien - devrait fonctionner sur n'importe quel système Linux et bien d'autres encore, mais s'il ne crée pas de canal nommé avec mkfifo
et l'utilise à la place, cela devrait également fonctionner.
La dernière chose qu'il fait est rm
le lien logiciel qu'il crée avant de quitter.
Cette version est en fait encore plus rapide sur mon système. Je suppose que c'est parce que bien qu'il exécute plus d'applications, il commence à leur transmettre immédiatement leurs arguments - alors qu'avant, il les empilait tous en premier.
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