Ergänzung mit 'sed'

40

Ich versuche, eine mathematische Operation mit auszuführen sed, aber meine Variablen werden weiterhin als Zeichenfolgen behandelt. Die Eingabe ist von dieser Art:

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

Ich möchte 15 als Ausgabe haben. Ich muss die Operation ausführen und das mathematische Ergebnis in nur einer Passage ersetzen, da ich das Programm als Python-Daemon stdoutausführe und Passagen wie das Umleiten auf Dateien, Öffnen dieser Dateien, Ausführen von Operationen, Extrahieren des Ergebnisses und Ausführen vermeiden möchte der Ersatz. Für mich sedscheint es das Beste zu sein, alles in einer Linie auszuführen.

Ich habe versucht, sowohl Input als auch Output auf verschiedene Arten zu übertragen

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

das Ergebnis war jedoch immer ein Ausdruck des zweiten Feldes.

Luigi Tiburzi
quelle
12
Es behandelt Ihre "Variablen" als Zeichenfolgen, denn das ist alles, was Sie tun müssen - Zeichenfolgenmanipulation. Es gibt kein Konzept für "Ganzzahl".
Kevin
2
Ich bin sehr gespannt, warum Sie verwenden möchten, um sedMathe zu tun
David Oneill
Ich dachte nur, dass es leicht Variablen werfen kann, wusste aber nicht, dass es so komplex ist!
Luigi Tiburzi

Antworten:

82

Wenn Sie ehrlich gesagt sed verwenden möchten, ist dies der richtige Weg:

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

Eingang:

1+2
100+250
100-250

Ausgabe:

3
350
-150

Ihre Mission ist es, Multiplikation zu implementieren, falls Sie dies akzeptieren möchten.

Simon Richter
quelle
5
+1 für die Herausforderung, liebe es! Vielleicht wäre das was für Code Golf ;-p
Tatjana Heuser
6
Und manche Leute sagen, dass Programmieren keine Mathematik ist. Dieses kleine Juwel widerlegt sie alle. Beste Nutzung von Base 1 aller Zeiten.
Bruce Ediger
1
Schön! - @ Simon: Ich fordere Sie auf, Tetration zu implementieren : P
AT
16
+1 Dies ist ein schönes Beispiel dafür, was ein Missverständnis, gepaart mit Kreativität, bewirken kann.
Rozcietrzewiacz
1
Die Multiplikation kann mit sed durchgeführt werden - und sie lässt sich auch recht gut auf große Zahlen skalieren!
Toby Speight
20

sedist hier nicht die beste Option, es führt keine native Arithmetik durch (siehe Inkrementieren einer Zahl, um zu erfahren, wie Sie dies möglicherweise tun könnten). Das könnte man machen mit awk:

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

Der beste zu verwendende Code hängt vom genauen Format Ihrer Eingabe ab und davon, was Sie tun möchten / müssen, wenn es sich nicht um einen numerischen Code handelt oder mehr als eine Zahl enthält.

Das kannst du auch nur mit bash:

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

oder exprin ähnlicher Weise verwenden.

Matte
quelle
17

Ich habe versucht, Ihre Herausforderung @Richter anzunehmen. Ich habe dazu einen Teil Ihres Codes verwendet:

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'

Eingang:

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

Ausgabe: alle richtigen Ergebnisse

Luigi Tiburzi
quelle
@ SimonRichter hoffe es gefällt euch !!
Luigi Tiburzi
Cross hat diese brillante Antwort hier gepostet: codegolf.stackexchange.com/a/39882/11259
Digitales Trauma
12

perlerlaubt ein sehr ähnliches Konstrukt wie sed's ... ein Unterschied ist, dass perles komplexere Dinge kann ... sedist sehr gut für einfache Textsubstitutionen

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

Ausgabe

a15
Peter.O
quelle
2
Dies kann auch ohne die Klammern erfolgen:perl -pe 's/[0-9]+/$&+3/e'
Glenn Jackman
8

Füttere einfach die Schnur in einen Taschenrechner

 echo 12 | sed 's/[0-9]*/&+3/' | bc
Glenn Jackman
quelle
Dies funktioniert nicht, wenn sich zwischen den Zahlen Text befindet.
Glenn Jackman
6

Ich verstehe wirklich nicht, warum die extreme Komplexität der akzeptierten Antwort, eine der folgenden Maßnahmen, was Sie wollen:

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

oder

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

Ich denke, es könnte GNU sed erfordern, aber ich bin nicht sicher.

michelpm
quelle
Es ist eine Gnu-Erweiterung.
Kevin
Ok, Sie haben Recht, aber die Antwort geht darüber hinaus, es implementiert den allgemeinen Zusatz nicht einen bestimmten, Sie können zwei beliebige Zahlen
eingeben
@LuigiTiburzi Es ist ziemlich einfach, dies auf "x + y" -Stileingaben zu verallgemeinern:echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Digitales Trauma
5

Wenn Sie reguläre Ausdrücke und arithmetische Operationen unbedingt kombinieren müssen, wählen Sie eine Sprache, in der der Ersetzungsparameter des regulären Ausdrucks eine Rückruffunktion sein kann.

Perl, Ruby, JavaScript und Python sind solche Sprachen:

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
Mann bei der Arbeit
quelle
1

Eine andere einfache bashLösung, die tatsächlich in einer Pipe funktioniert:

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

Wenn Sie in etwas Bashismus mischen:

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

So extrahieren Sie die Nummer aus einem Text:

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

Ohne sed nur bash:

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

Ersetzt jede Nicht-Ziffer ${var//[^0-9]/}und rechnet in doppelten runden Parens:$((x+3))

Benutzer unbekannt
quelle
2
Da ist kein Bashismus drin. $((...))wurde von POSIX eingeführt (der Bashismus ist $[...]). ${var//xxx/x}ist ein kshism, das auch von zsh und bash kopiert wurde. sed -rist ein GNUism
Stéphane Chazelas
0

Hier ist eine Perl-Lösung:

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

Wenn Sie die ersten Ziffern einer Zeichenfolge ändern möchten, können Sie Folgendes verwenden:

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

Wenn Sie es vorziehen, alle Zifferngruppen in einer Zeichenfolge zu ändern , können Sie den /gModifikator wie folgt verwenden:

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.
J L
quelle
0

Obwohl die Verwendung von sed expression großartig ist, hat es seine Grenzen. Zum Beispiel schlägt Folgendes fehl:

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

Um diese Einschränkung zu überwinden, greife ich einfach auf die eingebaute Potenz von pure sed zurück und implementiere folgenden Dezimaladdierer mit willkürlicher Länge:

#! / bin / sed -f

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

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

# dezimales Volladdierermodul
# EINGABE: 3-stellig (Carry in, A, B,)
# OUTPUT: 2bits (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 = 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 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 = 12167 = 14168 = 15169 = 16170 = 08171 = 09172 = 10173 = 11174 = 12175 = 13176 = 14177 = 15178 = 16179 = 17180 = 09181 = 10182 = 11183 = 12184 = 13185 = 14186 = 15187 = 16188 = 17189 = 18190 = 10191 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

Die Art und Weise, wie dies funktioniert, besteht darin, ein Dezimaladdierermodul zu implementieren, das zwei Eingangsziffern (A und B) sowie ein Übertragsbit hinzufügt und ein Summen- und ein Übertragsbit erzeugt. Die Idee stammt aus der Elektronik, wo Binäraddierer dasselbe für Binärzahlen tun. Alles, was wir tun müssen, ist, den Addierer über alle Ziffern zu schleifen, und wir können Zahlen beliebiger Länge hinzufügen (begrenzt durch den Speicher). Unten ist der Addierer in Aktion:

./decAdder.sed
666666666666666666666666666666999999999999991111111112222+1100000000000000000000011111111111111111111111111111111111
1766666666666666666666677777778111111111111102222222223333

In genau der gleichen Weise kann man einen binären Addierer (oder einen beliebigen anderen Basisaddierer) implementieren. Sie müssen nur die Zeile ersetzen, die s/$/;000=00001...mit dem richtigen Substitutionsmuster für die angegebene Basis beginnt . Zum Beispiel: s/$/;000=00001=01010=01011=10100=01101=10110=10111=11/ ist ein Substitutionsmuster für einen binären Addierer beliebiger Länge.

Sie können den Code, der auf meinem Github dokumentiert ist, anbringen .

Emsi
quelle