Aggiunta con 'sed'


40

Sto cercando di eseguire un'operazione matematica con sed, ma continua a trattare le mie variabili come stringhe. L'input è di questo tipo:

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

Mi piacerebbe avere 15 come output. Devo eseguire l'operazione e sostituirne il risultato matematico in un solo passaggio, perché sto eseguendo il programma come demone Python e voglio evitare passaggi come il reindirizzamento stdoutsu file, aprire quei file, eseguire operazioni, estrarre il risultato, fare il rimpiazzo. A me sedsembra il meglio per esibirsi tutto in una riga.

Ho provato a trasmettere input e output in vari modi come

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

ma il risultato è sempre stato una stampa del secondo campo.


12
Tratta le tue "variabili" come stringhe perché è tutto ciò che sed fa - manipolazione delle stringhe. Non ha il concetto di "intero".
Kevin,

2
Sono molto curioso di sapere perché vuoi usare la sedmatematica
David Oneill,

Ho solo pensato che potesse facilmente lanciare variabili, non mi ero reso conto che fosse così complesso!
Luigi Tiburzi,

Risposte:


82

Se vuoi sinceramente usare sed, allora questa è la strada da percorrere:

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

Ingresso:

1+2
100+250
100-250

Produzione:

3
350
-150

La tua missione, se scegli di accettarla, è implementare la moltiplicazione.


5
+1 per la sfida, adoralo! Forse sarebbe qualcosa per Code Golf ;-p
Tatjana Heuser,

6
E alcune persone dicono che la programmazione non è matematica. Questa piccola gemma le confuta tutte. Il miglior utilizzo di Base 1 di sempre.
Bruce Ediger,

1
Ben fatto! - @Simon: ti sfido a implementare la tetrazione : P
AT

16
+1 Questo è un bell'esempio di ciò che può generare un malinteso associato alla creatività.
rozcietrzewiacz,

1
La moltiplicazione è fatta con sed - e si adatta anche a numeri enormi abbastanza bene!
Toby Speight,

20

sednon è l'opzione migliore qui, non fa l'aritmetica in modo nativo (vedi Incrementare un numero per come potresti eventualmente farlo). Puoi farlo con awk:

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

Il miglior pezzo di codice da utilizzare dipenderà dal formato esatto dell'input e da ciò che si desidera / è necessario fare se non è numerico o contiene più di un numero, ecc.

Puoi anche farlo solo con bash:

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

o usando exprin modo simile.


17

Ho provato ad accettare la tua sfida @Richter, questo è quello che ho fatto usando parte del tuo codice:

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'

Ingresso:

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

Output: tutti i risultati corretti


@SimonRichter spero che ti piacerà !!
Luigi Tiburzi,

Cross ha pubblicato questa brillante risposta qui: codegolf.stackexchange.com/a/39882/11259
Digital Trauma

12

perlconsente un costrutto molto simile al sed... una differenza è che perlpuò fare cose più complesse ... sedè molto buono per semplici sottostazioni di testo

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

produzione

a15

2
può farlo anche senza le parentesi di cattura:perl -pe 's/[0-9]+/$&+3/e'
glenn jackman,

8

basta inserire la stringa in una calcolatrice

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

questo non funzionerà se c'è del testo tra i numeri.
Glenn Jackman,

6

Davvero non capisco perché l'estrema complessità della risposta accettata, nessuna delle due sotto faccia quello che vuoi:

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

o

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

Penso che potrebbe richiedere GNU sed, ma non ne sono sicuro.


È un'estensione gnu.
Kevin,

Ok hai ragione ma la risposta va oltre, implementa l'aggiunta generale non una particolare, puoi alimentare due numeri qualsiasi e otterrai il risultato
Luigi Tiburzi

@LuigiTiburzi È abbastanza semplice generalizzare questo input in stile "x + y":echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Trauma digitale

5

Se devi assolutamente combinare espressioni regolari e operazioni aritmetiche, scegli una lingua in cui il parametro di sostituzione dell'espressione regolare possa essere una funzione di callback.

Perl, Ruby, JavaScript e Python sono tali lingue:

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

1

Un'altra bashsoluzione semplice , che funziona davvero in una pipe:

 echo 12 | { read num; echo $(( num + 3)); }

1

Se mescoli un po 'di bashismo:

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

Per estrarre il numero da un testo:

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

Senza sed, bash:

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

sostituisce ogni non cifra ${var//[^0-9]/}e fa l'aritmetica in parentesi a doppio giro:$((x+3))


2
Non c'è bashismo lì dentro. $((...))è stato introdotto da POSIX (il bashismo è $[...]). ${var//xxx/x}è un kshism copiato anche da zsh e bash. sed -rè un GNUismo
Stéphane Chazelas,

0

Ecco una soluzione Perl:

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

Se si preferisce modificare la prima serie di cifre rilevate in una stringa, è possibile utilizzare:

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

Se si preferisce modificare tutte le serie di cifre in una stringa, è possibile utilizzare il /gmodificatore, in questo modo:

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.

0

Sebbene l'uso dell'espressione sed sia eccezionale, ha i suoi limiti. Ad esempio, il seguente errore non riesce:

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

Per ovviare a questa limitazione, mi rivolgo semplicemente al potere incorporato della pura sed e implementiamo il seguente sommatore decimale di lunghezza arbitraria:

#! / bin / sed -f

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

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

# modulo decimale adder completo
# INPUT: 3digits (carry in, A, B,)
# OUTPUT: 2 bit (Carry, Sum)
s / $ /;000 = 00001 = 01002 = 02003 = 03004 = 04005 = 05006 = 06007 = 07008 = 08009 = 09010 = 01011 = 02012 = 03013 = 04014 = 05015 = 06016 = 07017 = 08018 = 09019 = 10020 = 02021 = 03022 = 04023 = 05024 = 06025 = 07026 = 08027 = 09028 = 10029 = 11030 = 03031 = 04032 = 05033 = 06034 = 07035 = 08036 = 09037 = 10038 = 11039 = 12040 = 04.041 = 05.042 = 06043 = 07044 = 08045 = 09046 = 10047 = 11048 = 12049 = 13050 = 05.051 = 06052 = 07053 = 08054 = 09055 = 10056 = 11057 = 12058 = 13059 = 14060 = 06061 = 07062 = 08063 = 09.064 = 10065 = 11066 = 12067 = 13068 = 14069 = 15070 = 07071 = 08072 = 09073 = 10074 = 11075 = 12076 = 13077 = 14078 = 15079 = 16080 = 08081 = 09082 = 10083 = 11084 = 12085 = 13086 = 14087 = 15088 = 16089 = 17090 = 09091 = 10092 = 11093 = 12094 = 13095 = 14096 = 15097 = 16098 = 17099 = 18100 = 01101 = 02102 = 03103 = 04104 = 05105 = 06106 = 07107 = 08108 = 09.109 = 10110 = 02111 = 03112 = 04.113 = 05.114 = 06115 = 07.116 = 08.117 = 09.118 = 10119 = 11120 = 03121 = 04122 = 05123 = 06124 = = 08.126 = 07125 09127 = 10128 = 11129 = 12130 = 04131 = 05132 = 06133 = 07134 = 08.135 = 09.136 = 10137 = 11138 = 12139 = 13140 = 05141 = 06142 = 07143 = 08144 = 09.145 = 10146 = 11147 = 12148 = 13149 = 14150 = 06151 = 07152 = 08153 = 09154 = 10155 = 11156 = 12157 = 13158 = 14159 = 15160 = 07161 = 08162 = 09163 = 10164 = 11165 = 12166 = 13167 = 14168 = 15169 = 16170 = 08171 = 09.172 = 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 LOOP

Il modo in cui funziona è implementando il modulo sommatore decimale che aggiunge due cifre di input (A e B) oltre a Carry Bit e produce un bit Sum e Carry. L'idea è presa in prestito dall'elettronica in cui il sommatore binario fa lo stesso per i numeri binari. Tutto quello che dobbiamo fare è mettere in loop il sommatore su tutte le cifre e possiamo aggiungere numeri di lunghezza arbitrari (limitati dalla memoria). Di seguito è il sommatore in azione:

./decAdder.sed
666666666666666666666666666666999999999999991111111112222+1100000000000000000000011111111111111111111111111111111111
1766666666666666666666677777778111111111111102222222223333

Allo stesso modo si può implementare un sommatore binario (o qualsiasi altra base). Tutto quello che devi fare è sostituire la linea che inizia s/$/;000=00001...con un modello di sostituzione adeguato per una determinata base. Ad esempio: s/$/;000=00001=01010=01011=10100=01101=10110=10111=11/ è un modello di sostituzione per sommatore binario di lunghezza arbitraria.

Puoi adattare il codice documentato sul mio github .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.