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.


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.


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.


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


@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

2
También puede hacer esto sin los paréntesis de captura:perl -pe 's/[0-9]+/$&+3/e'
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.


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

1

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

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

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))


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.

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 .

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.