Supprimer des valeurs numériques dans certaines colonnes tout en conservant les signes less?

Je mets mes mains à être un débutant complet, alors excusez-moi si je fais mal.

Disons que j'ai le cadre de données suivant qui se poursuit indéfiniment horizontalement et verticalement avec des nombres négatifs seulement dans les colonnes impaires:

-1 2 3 4 -5 9 2 3 -4 5 -6 11 

Et je veux les 2ème, 4ème et 6ème colonnes complètes (ou toutes les colonnes paires) et les signes less des 1ère, 3ème et 5ème (ou toutes les colonnes impaires)

 - 2 4 - 9 3 - 5 - 11 

Et finir par finir avec ceci:

 -2 4 -9 3 -5 -11 

J'ai donc besoin des valeurs des colonnes paires inchangées et des colonnes impaires, s'il y a une valeur négative, gardez le – seulement et s'il y a une valeur positive, jetez-le.

Y at-il un moyen de le faire avec awk / sed?

C'est à peu près ce que je reçois:

 awk '{ for (i=2;i<=NF;i+=2) $i="" }1' FILE.txt | sed 's/[0-9,.]*//g' 

Voici une façon:

 $ awk '{for(i=1;i<=NF;i+=2){if($i<0){$i="-"}else{$i="";} }};1' file | sed 's/- */-/g; s/ */ /g' -2 4 -9 3 -5 -11 

Le script awk parcourt toutes les colonnes impaires et définit leur valeur - s'ils sont négatifs et vides sinon. Ensuite, le sed supprime les espaces suivant un - et remplace ensuite plusieurs espaces consécutifs par un seul. Notez que cela signifie que l'alignment sera brisé car certains champs auront deux caractères ou plus et d'autres en auront un. Ce ne sera pas un problème si vous travaillez avec des champs, ils n'ont tout simplement pas l'air joli.

La façon sed :

 sed -E ' s/^(([ \t]*-?[ \t]*[0-9.]+[ \t]+[0-9.]+)*)[ \t]+-?[ \t]*[0-9.]+$/\1/; s/[0-9.]+[ \t]+([0-9.]+)/\1/g' 

Sortie:

 -2 4 -9 3 -5 -11 

La première expression tue la colonne de fin s'il existe un nombre impair de colonnes. Il le fait en recherchant 0 ou plusieurs paires <number> <number> , où le premier nombre peut être négatif.

Edit: Une solution sed plus courte, inspirée par @mikeserv:

 sed -E ' s/[0-9.]+[ \t]*([0-9.]*)/\1/g; s/[- \t]*$//' 

La même chose avec perl :

 perl -lpe 's/^((\s*-?\s*[\d.]+\s*[\d.]+)*)\s+-?\s*[\d.]+$/$1/o; s/[\d.]+\s+([\d.]+)/$1/g' 

Une autre façon avec perl (probablement la plus propre):

 perl -lpe '$a = 1; s/([\d.]+\s*)/$a++ % 2 ? "" : $1/eg; s/[-\s]*$//o' 

Un perl :

 $ perl -anle 'BEGIN{$,=" "} print map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}grep{!($_%2)}0..$#F' file -2 4 -9 3 -5 -11 
  • @F input fractionnée à @F array
  • BEGIN{$,=" "} place le séparateur de champs de sortie dans un espace
  • grep{!($_%2)}0..$#F récupère tous les index @F dans le tableau @F , qui sont des index d'éléments impairs
  • map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"} vérifier si l'élément impair commence par - , alors append - à l'élément pair suivant, sinon ajoutez un espace

Comme réponse de @ terdon mais sans le sed:

 awk '{ for(i=1;i<=NF;i+=2){ if ($i<0) $(i+1)*=-1; $i = ""; } print }' 

Une solution python

 python -c 'from __future__ import print_function; import sys, math; for line in sys.stdin: x = [int(y) for y in line.split()] print(*[int(math.copysign(b, a)) for a, b in zip(x[::2], x[1::2])], sep=" ") ' <file 

Une solution simple awk basée sur les mathématiques:

 $ cat <<M | awk '{for(i=2;i<=NF;i+=2){printf "%4s",($(i-1)<0?-1:1)*$i}print ""}' -1 2 3 4 -5 9 2 3.2 -4 5 -6 M -2 4 -9 3.2 -5 
  • Boucle du second ( i=2 ) au dernier champ ( i<=NF ).
  • Multipliez le champ précédent ( $(i-1) ) par -1 ou 1.
  • Formatez bien la sortie ( printf "%4s" ) et imprimez une nouvelle ligne de return ( print "" ).

La seule mise en garde est que si vous avez un nombre impair de colonnes, le dernier champ n'affiche rien du tout. J'espère que c'est ce que vous attendez. Apparemment c'est ce que vous attendez. 🙂

(modifié pour fonctionner avec des valeurs décimales et pour aligner les conditions de boucle avec la question tout en sauvegardant 2 caractères).

Vous devez oublier le négatif entièrement – laissez-le. Vous souhaitez consolider deux champs – de gauche à droite. C'est très facile.

 sed ' s/ *\(.*\)/\1 / s/\([0-9]* *\)\{2\}/\1/g s/[ -]*$// ' <<\IN -1 2 3 4 -5 9 2 3 -4 5 -6 11 IN -2 4 -9 3 -5 -11 

Remarquez comment j'évite toute reference au signe – lorsque l'input est traitée, l'automate n'acceptera que des espaces ou des nombres car il ne comprend rien d'autre – tout le rest est totalement ignoré et restra en place.

Lorsque vous spécifiez un \{ intervalle de répétition numérique \} pour une \( sous-expression \) , seule la dernière occurrence de cette expression est \1 référencée. Ainsi, vous pouvez simplement presser – ou tronquer – un intervalle de répétition aussi facilement. Et parce que nous pressons la répétition derrière le signe – s'il y en a un – la deuxième occurrence de ce model suivra tout signe qui précédait le premier.

Le comportement décrit ci-dessus est spécifié par POSIX pour toutes les applications compatibles avec le BRE, mais très peu de sed se posent. GNU sed fait.

Enfin, les espaces sont juste pour rendre l'occurrence du motif régulière .

Bien sûr, cela ne marchera jamais pour vous. Ou, probablement plus correctement, cela marchera toujours pour vous, mais ne returnnera jamais aucun résultat. Comment pourrait-il si le schéma est indéfini ?