Además con 'sed'

40

Estoy tratando de realizar una operación matemática con sed, pero continúa tratando mis variables como cadenas. La entrada es de este tipo:

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

Me gustaría tener 15 como salida. Necesito hacer la operación y reemplazar su resultado matemático en un solo pasaje, porque estoy ejecutando el programa como un demonio Python, y quiero evitar pasajes como redirigir stdoutarchivos, abrir esos archivos, realizar operaciones, extraer el resultado, hacer el reemplazo. Para mí, sedparece el mejor para realizar todo en una línea.

He intentado emitir entradas y salidas de varias maneras, como

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

pero el resultado siempre fue una impresión del segundo campo.

Luigi Tiburzi
fuente
12
Está tratando sus "variables" como cadenas porque eso es todo lo que hace sed: manipulación de cadenas. No tiene el concepto de "entero".
Kevin
2
Tengo mucha curiosidad por qué quieres usar sedpara hacer matemáticas
David Oneill
Solo pensé que podía emitir variables fácilmente, ¡no me di cuenta de que era tan complejo!
Luigi Tiburzi

Respuestas:

82

Si honestamente quiere usar sed, este es el camino a seguir:

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

Entrada:

1+2
100+250
100-250

Salida:

3
350
-150

Su misión, si elige aceptarla, es implementar la multiplicación.

Simon Richter
fuente
55
+1 para el desafío, ¡me encanta! Tal vez eso sea algo para Code Golf ;-p
Tatjana Heuser
66
Y algunas personas dicen que la programación no es matemática. Esta pequeña joya los refuta a todos. El mejor uso de Base 1.
Bruce Ediger
1
¡Buena esa! - @Simon: te desafío a implementar la tetración : P
AT
16
+1 Este es un hermoso ejemplo de lo que puede generar un error combinado con la creatividad.
rozcietrzewiacz
1
La multiplicación se realiza con sed , ¡y también escala a grandes cantidades bastante bien!
Toby Speight
20

sedno es la mejor opción aquí, no hace aritmética de forma nativa (sin embargo, vea Incrementar un número para saber cómo podría hacerlo). Podrías hacer eso con awk:

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

El mejor código para usar dependerá del formato exacto de su entrada y de lo que quiera / necesite hacer si no es numérico, o contiene más de un número, etc.

También puede hacer esto solo con bash:

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

o usar exprde una manera similar.

Estera
fuente
17

Intenté aceptar tu desafío @Richter, esto es lo que hice usando parte de tu código:

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'

Entrada:

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

Salida: todos los resultados correctos

Luigi Tiburzi
fuente
@SimonRichter espero que disfrutes !!
Luigi Tiburzi
Cross publicó esta brillante respuesta aquí: codegolf.stackexchange.com/a/39882/11259
Digital Trauma
12

perlpermite una construcción muy similar a sedla de ... una diferencia es que perlpuede hacer cosas más complejas ... sedes muy bueno para las subtensiones de texto simples

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

salida

a15
Peter.O
fuente
2
También puede hacer esto sin los paréntesis de captura:perl -pe 's/[0-9]+/$&+3/e'
Glenn Jackman
8

solo introduce la cuerda en una calculadora

 echo 12 | sed 's/[0-9]*/&+3/' | bc
Glenn Jackman
fuente
esto no funcionará si hay texto entre los números.
Glenn Jackman
6

Realmente no entiendo por qué la extrema complejidad de la respuesta aceptada, cualquiera de los siguientes hace lo que quieres:

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

o

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

Creo que podría requerir GNU sed, pero no estoy seguro.

michelpm
fuente
Es una extensión de GNU.
Kevin
Ok, tienes razón, pero la respuesta va más allá, implementa la adición general, no una particular, puedes alimentar dos números y obtendrás el resultado
Luigi Tiburzi
@LuigiTiburzi Es bastante sencillo generalizar esto a la entrada de estilo "x + y":echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Digital Trauma
5

Si definitivamente tiene que combinar expresiones regulares y operaciones aritméticas, elija un idioma donde el parámetro de reemplazo de la expresión regular pueda ser una función de devolución de llamada.

Perl, Ruby, JavaScript y Python son estos lenguajes:

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
hombre trabajando
fuente
1

Otra bashsolución simple , que realmente funciona en una tubería:

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

Si mezclas algo de bashism:

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

Para extraer el número de un texto:

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

Sin sed, solo bash:

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

reemplaza todos los no dígitos ${var//[^0-9]/}y hace aritmética en pares dobles redondos:$((x+3))

usuario desconocido
fuente
2
No hay bashismo allí. $((...))fue presentado por POSIX (el bashism es $[...]). ${var//xxx/x}es un kshismo también copiado por zsh y bash. sed -res un GNUism
Stéphane Chazelas
0

Aquí hay una solución de Perl:

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

Si prefiere cambiar el primer conjunto de dígitos encontrados en una cadena, puede usar:

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

Si prefiere cambiar todos los conjuntos de dígitos en una cadena, puede usar el /gmodificador, de esta manera:

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
fuente
0

Aunque el uso de la expresión sed es excelente, tiene sus limitaciones. Por ejemplo, el siguiente error:

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

Para superar esta limitación, simplemente debo recurrir al poder incorporado de sed puro e implementar el siguiente sumador decimal de longitud arbitraria:

#! / bin / sed -f

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

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

# módulo sumador decimal completo
# ENTRADA: 3digits (Llevar, A, B,)
# SALIDA: 2bits (Llevar, Suma)
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 = 04041 = 05042 = 06043 = 07044 = 08045 = 09046 = 10047 = 11048 = 12049 = 13050 = 05051 = 06052 = 07053 = 08054 = 09055 = 10056 = 11057 = 12058 = 13059 = 14060 = 06061 = 07062 = 08063 = 09064 = 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 = 09109 = 10110 = 02111 = 03112 = 04113 = 05114 = 06115 = 07116 = 08117 = 09118 = 10119 = 11120 = 03121 = 04122 = 05123 = 06124 = 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 = 07161 = 08162 = 09163 = 10164 = 11165 = 12166 = 13167 = 10164 = 11165 = 12166 = 13167 = 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
sol
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

La forma en que funciona es mediante la implementación de un módulo sumador decimal que agrega dos dígitos de entrada (A y B), así como Carry Bit y produce un Sum y Carry bit. La idea está tomada de la electrónica donde el sumador binario hace lo mismo para los números binarios. Todo lo que tenemos que hacer es recorrer el sumador sobre todos los dígitos y podemos agregar números arbitrarios de longitud (limitado por la memoria). A continuación se muestra el sumador en acción:

./decAdder.sed
666666666666666666666666666666999999999999991111111112222+1100000000000000000000011111111111111111111111111111111111
1766666666666666666666677777778111111111111102222222223333

Exactamente de la misma manera se puede implementar un sumador binario (o cualquier otra base). Todo lo que tiene que hacer es reemplazar la línea que comienza s/$/;000=00001...con un patrón de sustitución adecuado para la base dada. Por ejemplo: s/$/;000=00001=01010=01011=10100=01101=10110=10111=11/ es un patrón de sustitución para sumador binario de longitud arbitraria.

Puede ajustar el código documentado en mi github .

Emsi
fuente