Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 25/09/2019, à 12:40

jeteztout

Faire des additions

Bonjour à tous,

je sais faire une addition en ligne de commande en utilisation la commande exp 3 + 5.

Mais je souhaiterais additionner tous les nombres (des centaines) contenus dans un fichier ASCII ( toto.txt ) et séparés par un simple espace.

Comment faire un tel script que je puisse pour pouvoir l'appeler à volonté, genre ./total.sh toto.txt ?

Merci beaucoup !

Hors ligne

#2 Le 25/09/2019, à 13:08

pingouinux

Re : Faire des additions

Bonjour,
Par exemple :

awk '{for(i=1;i<=NF;i++){s+=$i}}END{print s}' toto.txt

Édité :
Ou avec python

python3 -c "with open('toto.txt') as f: print(sum(map(int,f.read().split())))"

Dernière modification par pingouinux (Le 25/09/2019, à 13:17)

Hors ligne

#3 Le 25/09/2019, à 13:34

jeteztout

Re : Faire des additions

Super merci ! J'ai pris la solution awk. Du coup mon script shell ressemble à ça :

#!/bin/sh
  
awk '{for(i=1;i<=NF;i++){s+=$i}}END{print s}' $1

J'ai essayé d'envoyer le résultat dans un fichier externe avec echo, mais ça n'a pas marché pas ?

Hors ligne

#4 Le 25/09/2019, à 14:07

kamaris

Re : Faire des additions

Il faut que tu utilises une redirection vers ton fichier externe :

awk '{for(i=1;i<=NF;i++){s+=$i}}END{print s}' "$1" >fichier_externe

C'est aussi possible de faire le calcul en bash bien sûr :

read -a a <toto.txt
s=0; for n in "${a[@]}"; do ((s+=n)); done
echo "$s"

Je me suis amusé à faire un petit comparatif awk / python / bash. Sur ma machine, le résultat se fait clairement sentir à partir de 10 000 nombres dans toto.txt : si on définit

function f {
  echo 'awk'
  time awk '{for(i=1;i<=NF;i++){s+=$i}}END{print s}' toto.txt
  echo -e '\npython'
  time python3 -c "with open('toto.txt') as f: print(sum(map(int,f.read().split())))"
  echo -e '\nbash'
  time { read -a a <toto.txt; s=0; for n in "${a[@]}"; do ((s+=n)); done; echo "$s"; }
}

alors

$ f
awk
49995000

real	0m0,045s
user	0m0,027s
sys	0m0,009s

python
49995000

real	0m0,100s
user	0m0,083s
sys	0m0,011s

bash
49995000

real	0m0,221s
user	0m0,208s
sys	0m0,003s

Les résultat varient un peu selon les tirages, bien sûr, mais en gros c'est un bon facteur 2 de awk à python, et encore un bon facteur 2 de python à bash.

Dernière modification par kamaris (Le 25/09/2019, à 14:08)

Hors ligne

#5 Le 25/09/2019, à 14:57

jeteztout

Re : Faire des additions

Merci !

D'après vos résultats, awk est beaucoup plus performant donc ?

Bon enfin de toute façon, ça reste insignifiant en terme de temps wink

Hors ligne

#6 Le 25/09/2019, à 15:13

kamaris

Re : Faire des additions

Oui, awk est le plus rapide ici, tandis que python rivalise bien, mais bash est dans les choux. Ça se confirme sérieusement avec 100 000 nombres dans toto.txt :

$ f
awk
4999950000

real	0m0,176s
user	0m0,133s
sys	0m0,034s

python
4999950000

real	0m0,255s
user	0m0,197s
sys	0m0,043s

bash
4999950000

real	0m2,051s
user	0m1,970s
sys	0m0,041s

Python descend à moins d'un facteur 2 de awk, mais bash est à un facteur 8 de python. Et là, entre 2 secondes et 2 ou 3 dixièmes, tu commences à voir la différence. Surtout si ce bout de code est appelé plusieurs fois…

Hors ligne

#7 Le 25/09/2019, à 15:16

Postmortem

Re : Faire des additions

Salut,

kamaris a écrit :
read -a a <toto.txt
s=0; for n in "${a[@]}"; do ((s+=n)); done
echo "$s"

Là, on ne lit qu'une ligne du fichier, contrairement à ta solution en awk.


Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »

Hors ligne

#8 Le 25/09/2019, à 15:34

kamaris

Re : Faire des additions

Oui, je suis parti sur l'hypothèse d'une seule ligne quand j'ai lu « tous les nombres (des centaines) contenus dans un fichier ASCII ( toto.txt ) et séparés par un simple espace » dans le premier post.
Mais c'est vrai que ça n'est pas forcément évident, et que si ça n'est pas le cas, je compare des choses qui ne sont pas équivalentes.

Hors ligne

#9 Le 25/09/2019, à 15:41

credenhill

Re : Faire des additions

hello

sed 's/ /+/g' toto.txt | bc

Hors ligne

#10 Le 25/09/2019, à 16:16

Postmortem

Re : Faire des additions

C'est astucieux ça !


Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »

Hors ligne

#11 Le 25/09/2019, à 17:30

kamaris

Re : Faire des additions

Oui, et ça m'a donné une idée d'amélioration pour mon code bash :

read -a a <toto.txt
IFS='+'; echo "$(("${a[*]}"))"; unset IFS;

Comme ça, on est autour de 5 dixièmes de seconde pour 100 000 nombres, contre 2-3 dixièmes pour la soluce de credenhill, qui est comparable à celle en python.
Je précise d'ailleurs que dans ces 5 dixièmes, 3 vont au read et 2 vont à la sommation.

Dernière modification par kamaris (Le 25/09/2019, à 17:37)

Hors ligne

#12 Le 25/09/2019, à 18:14

credenhill

Re : Faire des additions

sans tableau

$ read a < toto.txt
$ echo $a
1 2 3 4 5
$ echo $((${a// /+}))
15 

Hors ligne

#13 Le 25/09/2019, à 19:09

Watael

Re : Faire des additions

kamaris: attention, unset IFS ne restaure pas l'IFS.

credenhill: au-delà d'un certain nombre de nombres ( smile ) cela ne fonctionne pas : le shell mouline, le CPU monte à 100%+; j'attends encore le résultat. allez! kill-9


Connected \o/
Welcome to sHell. · eval is evil.

Hors ligne

#14 Le 25/09/2019, à 19:34

kamaris

Re : Faire des additions

@Watael : Merci pour la remarque, je faisais effectivement l'erreur, en pensant que ça remettait IFS à sa valeur par défaut. J'avais mal interprété man bash qui dit :

- « If IFS is unset, the parameters are separated by spaces »
et
- « The default value is ‘‘<space><tab><newline>’’ »

en me disant « spaces == <space><tab><newline> ».

Je viens de vérifier et c'est effectivement faux. Du coup, existe-t-il une commande dédiée pour remettre IFS à sa valeur par défaut, ou faut-il dans tous les cas faire un swap via une variable tierce ? (puisqu'il reste de toutes façons le cas général, où IFS ne serait pas à sa valeur par défaut avant modification)

@credenhill : oui, la recherche de pattern dans une grande chaine en bash c'est horrible, il vaut mieux faire un sed. Même l'extraction d'une sous-chaine en connaissant l'index c'est exagérément long.

Dernière modification par kamaris (Le 25/09/2019, à 19:35)

Hors ligne

#15 Le 25/09/2019, à 20:03

Watael

Re : Faire des additions

pas que je sache, ni en vérifiant rapidement dans le man.
je ne le modifie que dans un sous-shell, ou je le sauvegarde avant de la modifier, puis je restaure la sauvegarde (!).


Connected \o/
Welcome to sHell. · eval is evil.

Hors ligne

#16 Le 25/09/2019, à 20:17

nany

Re : Faire des additions

Bonjour,



credenhill a écrit :

hello

sed 's/ /+/g' toto.txt | bc

Joli !
Il est où le bouton like ?

Hors ligne

#17 Le 25/09/2019, à 20:20

Watael

Re : Faire des additions

mouais. dégainer la calculatrice pour une addition... hmm


Connected \o/
Welcome to sHell. · eval is evil.

Hors ligne

#18 Le 25/09/2019, à 20:22

pingouinux

Re : Faire des additions

kamaris #14 a écrit :

Du coup, existe-t-il une commande dédiée pour remettre IFS à sa valeur par défaut

On peut exécuter les commandes dans un sous-shell, en les mettant entre ( ). Après la parenthèse fermante, IFS retrouve sa valeur d'origine.

( IFS='+'; commandes; )

ou

(
IFS='+'
commandes
)

Hors ligne

#19 Le 25/09/2019, à 20:29

kamaris

Re : Faire des additions

@pingouinux : oui, je fais ça quand je peux, mais dans le cas où on veut affecter une variable du shell appelant par exemple, on est coincé (à ma connaissance).
EDIT : mais dans le cas présent, effectivement, j'aurais pu (dû) le faire.

Dernière modification par kamaris (Le 25/09/2019, à 20:34)

Hors ligne

#20 Le 25/09/2019, à 20:38

Watael

Re : Faire des additions

c'est "l'export" depuis un sous-shell vers le shell parent qui n'est pas possible.

$ a="foo"
$ echo $(echo "$a"; b="bar") # la substitution de commande est toujours effectuée dans un sous-shell
foo
$ echo "$b"

$

t'imagine la galère si les variables du shell courant n'étaient pas disponibles dans un sous-shell ? smile

Dernière modification par Watael (Le 25/09/2019, à 20:39)


Connected \o/
Welcome to sHell. · eval is evil.

Hors ligne

#21 Le 25/09/2019, à 20:45

kamaris

Re : Faire des additions

Oui oui, c'est bien ce que je voulais dire (peut-être mal) par « affecter une variable du shell appelant » : modifier, dans le sous-shell, une variable du shell appelant (ou parent), et préserver cette modification une fois le sous-shell terminé. Ça, ça n'est pas possible.
Donc dans ce cas-là (au moins), on est obligé de passer par une variable temporaire pour IFS.

EDIT : hormis bien sûr le cas particulier où l'affectation de variable correspondrait au résultat du sous-shell dans son ensemble :

var=$(IFS='+'; …)

Dernière modification par kamaris (Le 25/09/2019, à 20:50)

Hors ligne

#22 Le 25/09/2019, à 20:54

Watael

Re : Faire des additions

`comprends pas. sad

$ ar=( 1 2 3 )
$ rslt=$(( $(IFS='+'; echo "${ar[*]}") ))
$ echo $rslt
6

les commandes dans le sous-shell sont exécutées et assignées via la substitution de commandes.

edit: croisement de messages

Dernière modification par Watael (Le 25/09/2019, à 20:55)


Connected \o/
Welcome to sHell. · eval is evil.

Hors ligne

#23 Le 25/09/2019, à 21:18

kamaris

Re : Faire des additions

Oui, on est bien d'accord que dans le cas présent, on peut passer par ou sous-shell, mais il me semble que ça n'est pas toujours possible.
Cependant, j'avoue que là tout de suite, j'ai du mal à trouver un exemple où ça ne serait vraiment pas possible.

Mais lorsque plusieurs commandes nécessitent un même changement d'IFS, avec des affectations de variables dans le lot, ça ne serait au moins pas pratique d'invoquer un sous-shell à chaque fois qu'on doit changer d'IFS, alors qu'il suffirait d'en changer une fois dans le shell principal, puis de revenir à la valeur par défaut une fois qu'on a passé toutes les commandes nécessitant ce changement.

Hors ligne

#24 Le 25/09/2019, à 21:31

Watael

Re : Faire des additions

dans ce cas, tu sauvegardes l'IFS, pour le restaurer après.

mais j'ai quand même du mal à imaginer un sous-shell qui retournerait plusieurs valeurs à assigner à plusieurs variables.
ça ne doit pas être une grande manipulation
avec un tableau ? avec un read ?

read v1 v2 v3 < <(IFS=$'\n'; cmd1; cmd2; cmd3)

?

à voir au cas par cas...


Connected \o/
Welcome to sHell. · eval is evil.

Hors ligne

#25 Le 28/09/2019, à 17:22

kamaris

Re : Faire des additions

C'est peut-être pinailler un peu, mais en fait la sauvegarde / restauration de l'IFS ne couvre pas tous les cas : si un unset IFS a eu lieu précédemment, alors un

IFS_bak=$IFS
…
IFS=$IFS_bak

ne restaurera pas l'IFS, mais sera équivalent à un IFS=''.

En toute rigueur, il faudrait gérer le cas unset, comme relevé dans ce post : https://unix.stackexchange.com/question … 947#264947

Hors ligne