#1 Le 25/09/2019, à 11: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, à 12: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, à 12:17)
Hors ligne
#3 Le 25/09/2019, à 12: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, à 13: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, à 13:08)
Hors ligne
#5 Le 25/09/2019, à 13: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
Hors ligne
#6 Le 25/09/2019, à 14: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, à 14:16
- Postmortem
Re : Faire des additions
Salut,
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, à 14: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, à 14:41
- credenhill
Re : Faire des additions
hello
sed 's/ /+/g' toto.txt | bc
Hors ligne
#10 Le 25/09/2019, à 15: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, à 16: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, à 16:37)
Hors ligne
#12 Le 25/09/2019, à 17: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, à 18:09
- Watael
Re : Faire des additions
kamaris: attention, unset IFS ne restaure pas l'IFS.
credenhill: au-delà d'un certain nombre de nombres ( ) 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, à 18: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, à 18:35)
Hors ligne
#15 Le 25/09/2019, à 19: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, à 19:17
- nany
Re : Faire des additions
Bonjour,
hello
sed 's/ /+/g' toto.txt | bc
Joli !
Il est où le bouton like ?
Hors ligne
#17 Le 25/09/2019, à 19:20
- Watael
Re : Faire des additions
mouais. dégainer la calculatrice pour une addition...
Connected \o/
Welcome to sHell. · eval is evil.
Hors ligne
#18 Le 25/09/2019, à 19:22
- pingouinux
Re : Faire des additions
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, à 19: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, à 19:34)
Hors ligne
#20 Le 25/09/2019, à 19: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 ?
Dernière modification par Watael (Le 25/09/2019, à 19:39)
Connected \o/
Welcome to sHell. · eval is evil.
Hors ligne
#21 Le 25/09/2019, à 19: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, à 19:50)
Hors ligne
#22 Le 25/09/2019, à 19:54
- Watael
Re : Faire des additions
`comprends pas.
$ 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, à 19:55)
Connected \o/
Welcome to sHell. · eval is evil.
Hors ligne
#23 Le 25/09/2019, à 20: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, à 20: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, à 16: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