[erledigt] Kreuzworträstel-Löser mit grep und bash

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
paedubucher
Beiträge: 938
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

[erledigt] Kreuzworträstel-Löser mit grep und bash

Beitrag von paedubucher » 07.09.2013 10:24:01

Hallo allerseits,

im Buch "The Linux Command Line" weist der Autor auf eine interessante Anwendung von grep hin: Kreuzworträtsel lösen.

Wetterphänomen mit acht Buchstaben, der dritte Buchstabe ist ein 'w' und der achte ein 'r':

Code: Alles auswählen

grep -i '^..w....r$' /usr/share/dict/ngerman
Abwasser
Abwässer
Anwender
Anwohner
Anwärter
Bewacher
Bewahrer
Beweiser
Bewerber
Bewohner
Erwerber
Gewinner
Gewitter
Gewänder
Gewässer
Juwelier
Newcomer
Umwohner
Unwetter
Urwälder
abwägbar
bewegter
etwaiger
gewagter
gewebter
gewehter
gewisser
umwogter
unwahrer
unwägbar
Gewitter!

Da nun '/usr/dict/share/ngerman' mühsam zu tippen ist und ich eh etwas Shell-Skripting üben möchte, habe ich mir folgendes Skript ausgedacht:

Code: Alles auswählen

#!/bin/bash

DICT=/usr/share/dict/ngerman

usage () {
    PROGNAME=$(basename $0)
    printf "usage: $PROGNAME regex\n"
}

if [ $# -le 0 ]; then 
    usage
    exit 1
fi

regex="'^$1$'"
grep -i $regex $DICT | while read word; do
    printf "$word\n"
done
Verwendet wird es dann etwa so:

Code: Alles auswählen

crosswords ..w....r
Das Problem ist, dass dieses Programm nichts ausgibt. Gebe ich jedoch die Variable $regex aus

Code: Alles auswählen

regex="'^$1$'"
echo "$regex"
Ergibt dies korrekterweise

Code: Alles auswählen

'^..w....r$'
Verwende ich die Ausgabe manuell mit grep, funktioniert alles prächtig. Das Problem muss also irgendwie am Aufruf von grep vor der Schleife sein.
Zuletzt geändert von paedubucher am 07.09.2013 16:58:18, insgesamt 1-mal geändert.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
detix
Beiträge: 1743
Registriert: 07.02.2007 18:51:28
Wohnort: MK

Re: Kreuzworträstel-Löser mit grep und bash

Beitrag von detix » 07.09.2013 11:01:45

Das Problem wird in der Variablendefinition "regex" sein, schreibs einfach so:

Code: Alles auswählen

regex="^$1$"
Gruß an alle Debianer, und immer daran denken:
Macht ohne Haftung funktioniert nicht!

Benutzeravatar
paedubucher
Beiträge: 938
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Kreuzworträstel-Löser mit grep und bash

Beitrag von paedubucher » 07.09.2013 11:07:31

So habe ich es auch schon probiert, und es funktioniert, aber: ich sollte ich grep die Regex nicht innerhalb von '' mitgeben, einfach um den Parameter zusammenzuhalten? Wenn ich es so auf der Konsole eingebe, funktioniert es ja schliesslich auch:

Code: Alles auswählen

grep -i '^..w...er$' /usr/share/dict/ngerman
Mir geht es eigentlich darum, warum ich das zwar auf der Kommandozeile schaffe, jedoch nicht im Skript.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
detix
Beiträge: 1743
Registriert: 07.02.2007 18:51:28
Wohnort: MK

Re: Kreuzworträstel-Löser mit grep und bash

Beitrag von detix » 07.09.2013 11:09:32

Wo ist das Problem, wenn es funktioniert?
Gruß an alle Debianer, und immer daran denken:
Macht ohne Haftung funktioniert nicht!

Benutzeravatar
paedubucher
Beiträge: 938
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Kreuzworträstel-Löser mit grep und bash

Beitrag von paedubucher » 07.09.2013 11:17:29

detix hat geschrieben:Wo ist das Problem, wenn es funktioniert?
Dass ich nicht verstehe, warum es nicht funktioniert. Mir geht es nicht darum, Kreuzworträtsel zu lösen, sondern darum, bash zu verstehen.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
detix
Beiträge: 1743
Registriert: 07.02.2007 18:51:28
Wohnort: MK

Re: Kreuzworträstel-Löser mit grep und bash

Beitrag von detix » 07.09.2013 11:43:33

Erklären kann ich das leider nicht,
an eigenen Skripten fummel ich nur selbst solange rum bis das Ergebnis passt.
Gruß an alle Debianer, und immer daran denken:
Macht ohne Haftung funktioniert nicht!

Benutzeravatar
paedubucher
Beiträge: 938
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Kreuzworträstel-Löser mit grep und bash

Beitrag von paedubucher » 07.09.2013 11:49:01

detix hat geschrieben:Erklären kann ich das leider nicht,
an eigenen Skripten fummel ich nur selbst solange rum bis das Ergebnis passt.
Das ist natürlich eine sehr pragmatische Einstellung und somit für alltägliche Probleme völlig angemessen. Ich befinde mich aber mit bash im Moment noch nicht im harten Alltag, sondern immer noch in der Schule :wink:
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Cae
Beiträge: 6349
Registriert: 17.07.2011 23:36:39
Wohnort: 2130706433

Re: Kreuzworträstel-Löser mit grep und bash

Beitrag von Cae » 07.09.2013 12:29:05

(Im folgenden Text gehoeren alle Anfuehrungszeichen und Ticks zum String dazu, es sind keine String-Begrenzer.) Bei "'foo'" erhaelt grep als Argument 'foo', weil die Shell die aeusseren Anfuehrungszeichen " selbst interpretiert und danach wegschneidet. Falls du 'foo' verwenden wuerdest, wuerde das auch passieren, und grep wuerde (wie gewuenscht) nur foo sehen. Allerdings wuerde $1 dann nicht expandiert werden.

Gruss Cae
If universal surveillance were the answer, lots of us would have moved to the former East Germany. If surveillance cameras were the answer, camera-happy London, with something like 500,000 of them at a cost of $700 million, would be the safest city on the planet.

—Bruce Schneier

Benutzeravatar
paedubucher
Beiträge: 938
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Kreuzworträstel-Löser mit grep und bash

Beitrag von paedubucher » 07.09.2013 13:44:56

Cae hat geschrieben: Falls du 'foo' verwenden wuerdest, wuerde das auch passieren, und grep wuerde (wie gewuenscht) nur foo sehen. Allerdings wuerde $1 dann nicht expandiert werden.
Aber $1 ist ja schon expandiert, die Variable $regex is nur noch eine Literale. Ich habe jetzt mal folgendes versucht, um die Situation zu verdeutlichen:

Code: Alles auswählen

regex="'^$1$'"
cmd="grep -i $regex $DICT"
echo $regex
echo $cmd
$($cmd) | while read word; do
    echo "$word"
done
Dann der Aufruf mit 'u.d':

Code: Alles auswählen

$ crossword u.d
'^u.d$'
grep -i '^u.d$' /usr/share/dict/ngerman
Wenn ich die letzte Zeile in die Shell kopiere, funktionier alles tadellos und das Wort 'und' wird gefunden.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
paedubucher
Beiträge: 938
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: [erledigt] Kreuzworträstel-Löser mit grep und bash

Beitrag von paedubucher » 07.09.2013 17:01:07

Jetzt verstehe ich das Problem endlich! Wenn ich einen Befehl im Skript aufrufe, werden die Quotes nicht expandiert! Tippe ich das manuell auf die Shell, wird es hingegen expandiert. Die Quotes bekommt grep gar nie zu sehen!

Code: Alles auswählen

$ cat > param.c
#include <stdio.h>
int main(int argc, char *argv[]) {
    puts(argv[1]);
    return 0;
}
[Ctrl-D]
$ make param-test
$ ./param-test 'foo'
foo
Immerhin wieder mal etwas gelernt :)
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

newdeb
Beiträge: 134
Registriert: 03.02.2011 11:11:21
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Frankfurt

Re: [erledigt] Kreuzworträstel-Löser mit grep und bash

Beitrag von newdeb » 08.09.2013 11:05:35

paedubucher hat geschrieben:Jetzt verstehe ich das Problem endlich! Wenn ich einen Befehl im Skript aufrufe, werden die Quotes nicht expandiert! Tippe ich das manuell auf die Shell, wird es hingegen expandiert. Die Quotes bekommt grep gar nie zu sehen!
Nein, so verhält sich die Shell nicht. Dein Denkfehler liegt an einer anderen Stelle.
Du definierst die Variable regex:

Code: Alles auswählen

regex="'^$1$'"
Wegen der Double-Quotes wird der Positionsparameter expandiert. Die inneren Single-Quotes interpretiert die Shell als normale Zeichen und fügt sie in $regex ein, ebenso die RegEx-Metazeichen ^ und $.
Der Inhalt von regex ist also jetzt (mit $1 = u.d):

Code: Alles auswählen

'^u.d$'
Und nach genau diesem Inhalt sucht das Kommando grep $regex, d.h. nach quotierten Begriffen mit den Zeichen ^ und $ am Anfang und Ende (die jetzt für grep keine Metazeichen mehr sind).

Code: Alles auswählen

echo und | grep $regex             # not found
echo \''^und$'\' | grep $regex    # found
'^und$'
Dein Denkfehler besteht in der Annahme, dass die Single-Quotes von der Shell wieder expandiert werden, und damit nicht mehr zum Suchmuster gehören. Aber die Shell macht keine mehrstufige Expansion (es sei denn, mann erzwingt dies).
Das zeigt auch dein Testprogramm "param":

Code: Alles auswählen

echo $(./param "'foo'")
'foo'
Ein anderes Beispiel:
$$ ist die Prozessnummer der Shell:

Code: Alles auswählen

PN='$$'
echo $PN
Wird die Prozessnummer ausgegeben? Nein, es erscheint nur $$, weil die Shell bei der Expansion den Ausdruck $PN durch den Inhalt $$ ersetzt, aber eben nicht in einer zweiten Stufe $$ duch die Prozessnummer. Man müsste das erzwingen mittels eval:

Code: Alles auswählen

PN='$$'
eval echo $PN
2232

Benutzeravatar
detix
Beiträge: 1743
Registriert: 07.02.2007 18:51:28
Wohnort: MK

Re: [erledigt] Kreuzworträstel-Löser mit grep und bash

Beitrag von detix » 08.09.2013 12:24:13

Sehr gut erklärt, newdeb (interessiert mich halt auch) :THX:
Ein als root beherztes

Code: Alles auswählen

echo "'^und$'" >>/usr/share/dict/ngerman
läßt die Suche als user fündig werden:

Code: Alles auswählen

crosswords u.d
'^und$'
So sieht das also aus...,
nicht das gewünschte Ergebnis, aber es wird klar und deutlich warum nicht.
Gruß an alle Debianer, und immer daran denken:
Macht ohne Haftung funktioniert nicht!

Antworten