Ajout avec 'sed'

40

J'essaie d'effectuer une opération mathématique avec sed, mais cela continue à traiter mes variables comme des chaînes. L'entrée est de ce genre:

$ echo 12 | sed 's/[0-9]*/&+3/'
$ 12+3

Je voudrais avoir 15 en sortie. Je dois effectuer l'opération et remplacer son résultat mathématique par un seul passage, car j'exécute le programme en tant que démon Python et je souhaite éviter les passages tels que la redirection stdoutsur des fichiers, l'ouverture de ces fichiers, l'exécution d'opérations, l'extraction du résultat, etc. le remplacement. Pour moi, sedsemble le meilleur pour effectuer tout en une seule ligne.

J'ai essayé de transtyper les entrées et les sorties de différentes manières, comme

$ echo 12 | sed 's/[0-9]*/int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'

mais le résultat était toujours une impression du deuxième champ.

Luigi Tiburzi
la source
12
Il traite vos "variables" comme des chaînes parce que c'est tout ce que sed fait - une manipulation de chaîne. Il n'a pas de notion de "entier".
Kevin
2
Je suis très curieux de savoir pourquoi vous voulez utiliser sedles mathématiques
David Oneill
Je pensais simplement que cela pouvait facilement lancer des variables, mais je ne réalisais pas que c'était si complexe!
Luigi Tiburzi

Réponses:

82

Si vous voulez honnêtement utiliser sed, voici le chemin à suivre:

s/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/+//g
: minus
s/|-|/-/g
t minus
s/-$//
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0\1/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back

Contribution:

1+2
100+250
100-250

Sortie:

3
350
-150

Votre mission, si vous choisissez de l'accepter, est de mettre en œuvre la multiplication.

Simon Richter
la source
5
+1 pour le défi, j'adore! Peut-être que ce serait quelque chose pour Code Golf ;-p
Tatjana Heuser
6
Et certaines personnes disent que la programmation n'est pas mathématique. Ce petit bijou les réfute tous. Meilleure utilisation de la base 1 de tous les temps.
Bruce Ediger
1
Joli! - @Simon: je vous mets au défi de mettre en œuvre la pénétration : P
AT
16
+1 Ceci est un bel exemple de ce qu'une idée fausse associée à la créativité peut engendrer.
rozcietrzewiacz
1
La multiplication doit être faite avec sed - et cela permet d’évoluer très bien aussi!
Toby Speight le
20

sedCe n’est pas la meilleure option ici, il ne fait pas l’arithmétique de façon native (voir Incrémenter un nombre pour savoir comment vous pouvez éventuellement le faire). Vous pouvez le faire avec awk:

$ echo 12 | awk '{print $0+3}'
15

Le meilleur morceau de code à utiliser dépendra du format exact de votre entrée et de ce que vous voulez / devez faire s'il n'est pas numérique, s'il contient plus d'un nombre, etc.

Vous pouvez également le faire uniquement avec bash :

$ echo $(( $(echo 12) + 3 ))

ou en utilisant exprd'une manière similaire.

Tapis
la source
17

J'ai essayé d'accepter votre défi @Richter, voici ce que j'ai fait en utilisant une partie de votre code:

sed 's/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/.*\*$/0/
s/^\*.*/0/
s/*|/*/
: mult
s/\(|*\)\*|/\1<\1*/ 
t mult
s/*//g
s/<//g
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0\1/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back'

Contribution:

04*3
4*3
40*3
42*32
150*20
1*3
3*1
0*3
3*0

Sortie: tous les résultats corrects

Luigi Tiburzi
la source
@SimonRichter espérons que vous apprécierez !!
Luigi Tiburzi le
Cross a posté cette réponse brillante ici: codegolf.stackexchange.com/a/39882/11259
Digital Trauma
12

perlpermet une construction très similaire à celle sedde ... une différence est que l' perlon peut faire des choses plus complexes ... sedc'est très bien pour les simples sous-textes

 echo 'a12' | perl -pe 's/([0-9]+)/($1+3)/e'  # the trailing /e means evaluate

sortie

a15
Peter.O
la source
2
peut également le faire sans les parenthèses de capture:perl -pe 's/[0-9]+/$&+3/e'
Glenn Jackman
8

juste introduire la ficelle dans une calculatrice

 echo 12 | sed 's/[0-9]*/&+3/' | bc
Glenn Jackman
la source
cela ne fonctionnera pas s'il y a du texte entre les nombres.
Glenn Jackman
6

Je ne comprends vraiment pas pourquoi l'extrême complexité de la réponse acceptée, l'une ou l'autre des réponses ci-dessous fait ce que vous voulez:

echo 12 | sed 's/[0-9]*/echo \$(( & + 3 ))/e'

ou

echo 12 | sed 's/[0-9]*/expr & + 3/e'

Je pense que cela pourrait nécessiter GNU sed, mais je ne suis pas sûr.

michelpm
la source
C'est une extension de gnou.
Kevin
Ok, vous avez raison, mais la réponse va au-delà, elle implémente l'addition générale et non une particulière, vous pouvez saisir n'importe quel nombre et vous obtiendrez le résultat
Luigi Tiburzi
@LuigiTiburzi Il est assez simple de généraliser ceci à une entrée de style "x + y":echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Digital Trauma
5

Si vous devez absolument combiner des expressions régulières et des opérations arithmétiques, choisissez un langage dans lequel le paramètre de remplacement de l'expression régulière peut être une fonction de rappel.

Perl, Ruby, JavaScript et Python sont de tels langages:

bash-4.2$ echo 12 | perl -pe 's/\d+/$&+3/e'
15

bash-4.2$ echo 12 | ruby -pe '$_.sub!(/\d+/){|s|s.to_i+3}'
15

bash-4.2$ echo 12 | js -e 'print(readline().replace(/\d+/,function(s){return parseInt(s)+3}))'
15

bash-4.2$ echo 12 | python -c 'import re;print re.sub("\d+",lambda s:str(int(s.group(0))+3),raw_input())'
15
homme au travail
la source
1

Une autre bashsolution simple , qui fonctionne réellement dans un tuyau:

 echo 12 | { read num; echo $(( num + 3)); }
rozcietrzewiacz
la source
1

Si vous mélangez un peu de bashisme:

echo $(($(echo 12 | sed 's/[0-9]*/&+3/')))

Pour extraire le numéro d'un texte:

echo $(($(echo "foo12bar" | sed -r 's/[^0-9]*([0-9]*).*/\1+3/')))

Sans sed, juste bash:

var="foo12bar"
echo $((${var//[^0-9]/}+3))

remplace chaque non-chiffre ${var//[^0-9]/}et fait de l'arithmétique en doubles rondes:$((x+3))

Utilisateur inconnu
la source
2
Il n'y a pas de bashisme là-dedans. $((...))a été introduit par POSIX (le bashisme est $[...]). ${var//xxx/x}est un kshism également copié par zsh et bash. sed -rest un GNUism
Stéphane Chazelas le
0

Voici une solution Perl:

echo 12 | perl -wlpe '$_ += 3'
# Output:  15

Si vous préférez modifier le premier ensemble de chiffres rencontrés dans une chaîne, vous pouvez utiliser:

echo I am 12 years old. | perl -wlpe 's/(\d+)/$1 + 3/e'
# Output:  I am 15 years old.

Si vous préférez changer tous les ensembles de chiffres d'une chaîne, vous pouvez utiliser le /gmodificateur, comme ceci:

echo They are 11, 12, and 13 years old. | perl -wlpe 's/(\d+)/$1 + 3/eg'
# Output:  They are 14, 15, and 16 years old.
JL
la source
0

Bien que l'utilisation de sed soit excellente, elle a ses limites. Par exemple, la suite échoue:

$ echo "1000000000000000000000000000000+1" | sed -e 's/\([0-9]*\)+\([0-9]*\)/expr \1 + \2/e'
expr: 1000000000000000000000000000000: Numerical result out of range

Pour surmonter cette limitation, il suffit de se tourner vers la puissance intrinsèque de sed sed et de mettre en œuvre l'additionneur décimal de longueur arbitraire suivant:

#! / bin / sed -f

s / + / \ n / g
s / $ / \ n \ n0 /

:BOUCLE
s / ^ \ (. * \) \ (. \) \ n \ (. * \) \ (. \) \ n \ (. * \) \ n \ (. \) $ / 0 \ 1 \ n0 \ 3 \ n \ 5 \ n \ 6 \ 2 \ 4 /
h
s /^.* \ n. * \ n. * \ n \ (... \) $ / \ 1 /

# module d'additionneur décimal complet
# INPUT: 3 chiffres (Carry in, A, B,)
# SORTIE: 2bits (Carry, Sum)
s / $ /;000 = 00001 = 01002 = 02003 = 03004 = 04005 = 05006 = 06007 = 07008 = 08009 = 09010 = 01011 = 02012 = 03013 = 04014 = 05016 = 06016 = 07017 = 08018 = 09019 = 02019 = 02019 = 02021 = 03021 = 06025 = 07026 = 08027 = 09028 = 10029 = 11030 = 03031 = 04032 = 05033 = 06034 = 07035 = 08036 = 09037 = 10038 = 11039 = 12040 = 04042 = 05042 = 06043 = 07044 = 07044 = 08045 = 04045 = 10045 = 10048 = 10048 = 10048 = 10048 = 10048 = 10048 = 10048 = 10048 = 10048 = 10048 = 10048 = 10040 = 04041 = 04042 = 06042 = 06042 = 04034 = 04034 = 0 13050 = 05051 = 06052 = 07053 = 08054 = 09055 = 10056 = 11056 = 12058 = 13059 = 14060 = 06061 = 07062 = 08064 = 09064 = 10065 = 11066 = 12067 = 13067 = 13069 = 06062 = 06062 = 07055 = 09054 = 11075 = 12076 = 13077 = 14078 = 15079 = 16080 = 08081 = 09082 = 10083 = 11084 = 12085 = 13086 = 14087 = 15088 = 15089 = 17090 = 09091 = 10092 = 11093 = 12094 = 13094 = 13096 = 15097 = 16098 = 17099 = = 18100 = 01101 = 02102 = 03103 = 04104 = 05105 = 06106 = 07107 = 08108 = 09109 = 10110 = 0113 = 0113 = 05115 = 07117 = 08118 = 08117 = 09119 = 10119 = 10110 = 0113 = 04114 = 06115 = 04124 = 05121 = 05121 = 05121 07125 = 08126 = 09127 = 10128 = 11129 = 12130 = 04131 = 05132 = 06133 = 07134 = 08135 = 09136 = 10137 = 11138 = 12139 = 13140 = 05141 = 06142 = 07143 = 08144 = 09145 = 10146 = 11147 = 12148 = 13149 = 14150 = 06151 = 07152 = 08153 = 09154 = 10155 = 11156 = 12157 = 13158 = 14159 = 15160 = 07162 = 08162 = 09162 = 09163 = 10164 = 11165 = 11166 = 11166 = 11167 = 11167 = 11167 = 11167 = 13167 = 13167 = 11167 = 13167 = 13167 = 11167 = 11167 = 13167 = 11166 = 11167 = 11167 = 14168 = 15169 = 16170 = 08171 = 09172 = 10173 = 11174 = 12175 = 13176 = 14177 = 15178 = 16179 = 17180 = 09181 = 10182 = 11183 = 12184 = 13185 = 14186 = 15187 = 16188 = 17189 = 18190 = 10191 = 11192 = 12193 = 13194 = 14195 = 15196 = 16197 = 17198 = 18199 = 19 /
s / ^ \ (... \) [^;] *; [^;] * \ 1 = \ (.. \). * / \ 2 /
H
g
s / ^ \ (. * \) \ n \ (. * \) \ n \ (. * \) \ n ... \ n \ (. \) \ (. \) $ / \ 1 \ n \ 2 \ n \ 5 \ 3 \ n \ 4 /
/ ^ \ ([0] * \) \ n \ ([0] * \) \ n / {
        s /^.* \ n. * \ n \ (. * \) \ n \ (. \) / \ 2 \ 1 /
        s / ^ 0 \ (. * \) / \ 1 /
        q
}
b boucle

Cela fonctionne en mettant en œuvre un module d'additionneur décimal qui ajoute deux chiffres d'entrée (A et B) ainsi que Carry Bit et produit un bit Sum et Carry. L'idée est empruntée à l'électronique où l' additionneur binaire fait la même chose pour les nombres binaires. Tout ce que nous avons à faire est de boucler l'additionneur sur tous les chiffres et nous pouvons ajouter des nombres de longueur arbitraires (limités par la mémoire). Voici l'additionneur en action:

./decAdder.sed
666666666666666666666666666666999999999999991111111112222+1100000000000000000000011111111111111111111111111111111111
1766666666666666666666677777778111111111111102222222223333

De la même manière, on peut implémenter un additionneur binaire (ou n’importe quelle autre base). Tout ce que vous avez à faire est de remplacer la ligne qui commence s/$/;000=00001...par un modèle de substitution approprié pour une base donnée. Par exemple: s/$/;000=00001=01010=01011=10100=01101=10110=10111=11/ est un modèle de substitution pour un additionneur binaire de longueur arbitraire.

Vous pouvez adapter le code documenté sur mon github .

Emsi
la source