Comment sortiriez-vous la diagonale d'un file?
Par exemple, j'ai un file avec l'intérieur suivant.
1,2,3,4,5 6,7,8,9,0 1,2,3,4,5 6,7,8,9,0 1,2,3,4,5
La sortie serait censée ressembler à: 1 7 3 9 5
ou quelque chose comme ça.
Je peux sortir une colonne via cut (cut -d "," -f5 filename), mais je ne sais pas quoi écrire pour ne sortir que la diagonale.
awk solution, pas aussi élégante que la solution @don_chrissti, mais fonctionne là où pas un carré.
awk -F, '{a=a$++n" "}END{print a}' file
Les données d'input que nous examinons peuvent être traitées comme une masortingce ou un tableau bidimensionnel. Maintenant, si nous abordons le problème de ce sharepoint vue, il existe plusieurs outils de calcul qui peuvent être utilisés pour manipuler les masortingces. En particulier, le module numpy
de Python le permet. Ainsi, nous pourrions utiliser deux choses – la fonction loadtxt()
et la diagonal()
pour extraire datatables désirées:
$ python -c 'import sys,numpy;a=numpy.loadtxt(sys.argv[1],dtype=int,delimiter=",");print( a.diagonal() )' input.txt [1 7 3 9 5]
Maintenant, c'est le gros morceau du travail fait. Pour rendre la sortie jolie, il suffit de convertir datatables obtenues en strings, et de créer une string séparée par des espaces individuels. Ainsi:
$ python -c 'import sys,numpy;a=numpy.loadtxt(sys.argv[1],delimiter=",");print(" ".join( [ str(int(i)) for i in a.diagonal() ]))' input.txt 1 7 3 9 5
Bien sûr, tout cela n'a pas besoin d'être fait en une seule ligne. Pour des raisons de lisibilité, nous pouvons créer un petit script qui nous permettra également de traiter tous les noms de files donnés en arguments sur la command line:
#!/usr/bin/env python import sys import numpy as np for filename in sys.argv[1:]: data=np.loadtxt(filename,delimiter=",") diag = data.diagonal() result_ssortingng = " ".join( [ str(int(i)) for i in diag ] ) print(result_ssortingng)
sed -ne ' y/,/\n/;G;s/\n$// y/\n_/_\n/;:loop /_$/{ s/// s/^[^_]*_// bloop } y/\n_/_\n/;P s/.*//;H ' input.file | paste -sd' '
Nous maintenons un ledger sur les champs séparés par des virgules pour sauter dans l'espace de motif dans l'espace de cale.
Le bouclage coupe l'espace du motif des deux extrémités pour arriver à la situation lorsque le plus à gauche est prêt pour l'printing. Nous pouvons imaginer qu'il brûle une bougie des deux extrémités (cependant, le taux de combustion est différent). De l'avant, nous coupons un champ séparé par des virgules, tandis que de la fin, nous lâchons un \n
. La gravure continue jusqu'à ce qu'il n'y ait plus de nouvelles lignes de fuite.
Et maintenant l'élément diagonal est assis à l'avant de l'espace de model.
L'artefact y/\n_/_\n/
est de contourner le fait que POSIX sed
n'a pas la nouvelle ligne annulée à l'intérieur d'une class de caractères, [^\n]
.
Comme dernière étape pour la ligne actuelle, la zone de maintien est incrémentée. La command paste
est d'get la sortie sur une seule ligne.
Si tous les champs sont numériques dans votre csv, vous pouvez également utiliser l'extrait suivant de dc
. Le tr
est de supprimer les virgules car les champs dc sont délimités par des espaces et les nombres négatifs commencent par _ plutôt que –
tr ',-' ' _' < file | dc -e '[q]sq [s0zlk<a]sa [?z0=qzlk1+dsk<an32ancz0=?]s? 0skl?x[]p'
On définit 3 macros, q
pour quitter quand on a fini, a
pour une boucle pour supprimer les éléments de la fin (popping), et ?
pour configurer une boucle pour effectuer une lecture orientée vers la ligne et invoquer la macro a
, puis imprimer l'élément diagnol maintenant exposé.
tr ... | dc -e ' # quit macro [q]sq # macro to pop elements from stack till they are more than counter k [s0 zlk<a]sa # do-while loop for reading lines [ ?z0=q # quit if line read is empty zlk1+dsk<a # increment the counter k and compare it against NF (z) call a if > n 32an # print the diagonal element and a space (no newlines) c z0=? # invoke myself again ]s? # main 0sk # initialize the counter k l?x # call the macro ? to start the file read-in loop []p # print a trailing newline '
Sortie:
1 7 3 9 5
Shell POSIX :
n=0 while IFS=',' && read x ; do set -- $x ; shift $n ; echo "$1" ; n=$((n+1)) done < inputfile
bash
peut être plus concis:
n=0; while IFS=',' read -ax ; do echo "${x[((n++))]}" ; done < inputfile
Notez qu'il est facile d'get des diagonales pour les quatre rotations des données en filtrant vers tac
et rev
, (ou tac
seul – si datatables sont un tableau carré .csv ). Exemples de shell POSIX , mais faites d'abord de nouvelles valeurs d'input asymésortingques:
seq 25 | paste -d, - - - - - | tee asym_input
Sortie:
1,2,3,4,5 6,7,8,9,10 11,12,13,14,15 16,17,18,19,20 21,22,23,24,25
A \
gauche à droite en diagonale , (la question OP , avec des inputs différentes):
n=0 while IFS=',' && read x ; do set -- $x ; shift $n ; echo "$1" ; n=$((n+1)) done < asym_input
Sortie:
1 7 13 19 25
A /
diagonale de gauche à droite :
n=0 tac asym_input | while IFS=',' && read x ; do set -- $x ; shift $n ; echo "$1" ; n=$((n+1)) done
Sortie:
21 17 13 9 5
A /
droite à gauche diagonale :
n=0 rev asym_input | while IFS=',' && read x ; do set -- $x ; shift $n ; echo "$1" ; n=$((n+1)) done | rev
Sortie:
5 9 13 17 21
A \
droite à gauche diagonale :
n=0 tac asym_input | rev | while IFS=',' && read x ; do set -- $x ; shift $n ; echo "$1" ; n=$((n+1)) done | rev
Sortie:
25 19 13 7 1
awk -F, '{printf(NR==1?$NR:" "$NR)}END{printf("\n")}' file
Rectangular:
awk -F, ' NR==1{printf($1);next} {printf(" "$(NR>NF?NF:NR))}END{printf("\n")} ' file`
Autres diagonales:
awk -F, -vdiag=9 -vdir=-1 ' {d=(NR-1)*(dir>0?1:-1)+1+diag;d=(d<1?1:d);d=(d>NF?NF:d)} {printf("%s%s",NR==1?"":" ",$d)} END {printf("\n")} ' file
Posix qui sélectionne le nombre diagonal et la direction /
vs \
. (le code est long, merci de le lire à la fin de ce post).
Avec awk, la solution la plus élégante est la suivante:
$ awk -F, '{print $NR}' file 1 7 3 9 5
Pour avoir une sortie d'une ligne, vous pourriez faire (avec un espace de fuite):
$ awk -F, -v ORS=" " '{print $NR}' file; echo 1 7 3 9 5
Si vous devez avoir la sortie sans espaces de fin:
$ awk -F, '{printf(NR==1?$NR:" "$NR)}END{printf("\n")}' file 1 7 3 9 5
Pour un file avec, par exemple, ceci:
$ cat file 1,2,3,4,5 6,7,8,9,0 1,2,3,4,5 6,7,8,9,0 1,2,3,4,5 a,b,c,d,e f,g,h,i,j k,l,m,n,o p,q,r,s,t u,v,w,x,y
La solution ci-dessus imprimera des espaces blancs:
$ awk -F, '{printf(NR==1?$NR:" "$NR)}END{printf("|\n")}' file 1 7 3 9 5 |
Si ce que vous voulez dans ce cas est d'arrêter le traitement, alors vérifier que le nombre d'loggings est plus grand que le nombre de champs peut être une solution (si le nombre de champs change pour chaque ligne):
$ awk -F, 'NR>NF{exit}{printf(NR==1?$NR:" "$NR)}END{printf("|\n")}' infile 1 7 3 9 5|
Si vous voulez imprimer le dernier champ de n'importe quelle ligne où NR> NF:
$ awk -F, 'NR==1{printf($1);next}{printf(" "$(NR>NF?NF:NR))}END{printf("|\n")}' file 1 7 3 9 5 ejoty|
Si l'on a besoin d'une diagonale différente de la diagonale principale, on peut signaler que la variable diag a une valeur différente de 0 (0 est la diagonale principale de ce code):
$ awk -F, -vdiag=3 ' {d=NR+diag;d=(d<1?1:d);d=(d>NF?NF:d)} {printf("%s%s",NR==1?"":" ",$d)} END {printf("\n")} ' file 4 0 5 0 5 ejoty
Notez que la valeur de diag pourrait être négative:
$ awk -F, -vdiag=-3 ' {d=NR+diag;d=(d<1?1:d);d=(d>NF?NF:d)} {printf("%s%s",NR==1?"":" ",$d)} END {printf("\n")} ' infile 1 6 1 6 2 cioty
Et la diagonale pourrait être comme /
au lieu de \
avec plus de maths:
$ awk -F, -vdiag=4 -vdir=-1 ' {d=(NR-1)*(dir>0?1:-1)+1+diag;d=(d<1?1:d);d=(d>NF?NF:d)} {printf("%s%s",NR==1?"":" ",$d)} END {printf("\n")} ' file 5 9 3 7 1 afkpu $ awk -F, -vdiag=9 -vdir=-1 ' {d=(NR-1)*(dir>0?1:-1)+1+diag;d=(d<1?1:d);d=(d>NF?NF:d)} {printf("%s%s",NR==1?"":" ",$d)} END {printf("\n")} ' infile 5 0 5 0 5 eimqu
Avec un file d'input différent:
$ printf '%s\n' {1..6}{1..5} 7{1..3} | pr -ta -5 -s',' | tee inputfile 11,12,13,14,15 21,22,23,24,25 31,32,33,34,35 41,42,43,44,45 51,52,53,54,55 61,62,63,64,65 71,72,73
Un équivalent de awk dans le shell compatible Posix peut être:
diagPosix(){ diag=${1%%[!0-9+-]*} dir=$(((${2:-1}>0)?1:-1)) n=0 a="" while read x ; do # echo "testing $n $x" IFS=',' eval 'set -- $x' # Place values in pos parms. a=$(( diag + n*dir )) # calculate position a b=$(( (a<0)?0:a )) # low limit is zero (0) d=$(( (b>$#-1)?$#-1:b )) # upper limit is ($#-1) # echo "a=$ab=$bd=$d #=$# n=$n" shift $d # remove leading parms printf '%s' "$s" "$1" # print parm (and an space) s=" " # Next loop will have space. n=$((n+1)) # In which line are we? done <"${3:-inputfile}" echo } diagPosix "$@"
qui, avec l'input ci-dessus, fonctionnera comme suit:
$ ./script 0 1 inputfile 11 22 33 44 55 65 73 $ ./script -2 1 inputfile 11 21 31 42 53 64 73 $ ./script 4 -1 inputfile 15 24 33 42 51 61 71
Le code a été testé dans certaines coquilles et fonctionne bien.
ash : 11 22 33 44 55 65 73 /usr/bin/yash : 11 22 33 44 55 65 73 y2sh : 11 22 33 44 55 65 73 dash : 11 22 33 44 55 65 73 zsh/sh : 11 22 33 44 55 65 73 b203sh : 11 22 33 44 55 65 73 b204sh : 11 22 33 44 55 65 73 b205sh : 11 22 33 44 55 65 73 b30sh : 11 22 33 44 55 65 73 b32sh : 11 22 33 44 55 65 73 b41sh : 11 22 33 44 55 65 73 b42sh : 11 22 33 44 55 65 73 b43sh : 11 22 33 44 55 65 73 b44sh : 11 22 33 44 55 65 73 lksh : 11 22 33 44 55 65 73 mksh : 11 22 33 44 55 65 73 ksh93 : 11 22 33 44 55 65 73 attsh : 11 22 33 44 55 65 73 zsh/ksh : 11 22 33 44 55 65 73
Il échoue pour zsh (pas dans l'émulation) à la fois parce que zsh ne se divise pas par défaut et parce que la numérotation des arrays commence à 1 (pas à 0). Il a été testé dans csh et tcsh mais cela ne fonctionne pas.
Et il ne devrait pas y travailler (n'utilisez pas csh pour les scripts!).
Les solutions qui fonctionnent de bas en haut doivent être faciles à build en utilisant tac sur l'input.
Voici une autre alternative utilisant awk:
awk -F, '{printf "%s%s",$NR,NR==NF?ORS:OFS}' file 1 7 3 9 5
Une solution shell / coupe:
index=1 while read line do cut -d, -f ${index} <<< "$line" index=$((++index)) done < input