Sida 1 av 1

Bashskript för synonymordlista

Postat: 22 maj 2009, 19:43
av Xappe
Hej!

Det klagas mycket på att det inte finns någon vettig synonymordlista i linux (främst open office). Råkade surfa förbi synonymer.se idag och tänkte att den kan man säkert använda till nåt bra. :)

Har skrivit ett rätt enkelt bashskript som verkar fungera så långt att det ger tillbaka synonymer till ordet man ger som argument till skriptet. Då jag inte är någon expert på skriptande tar jag gärna emot tips och råd på hur man kan göra skriptet bättre och snyggare.

När allt fungerar tillfredsställande funderar jag på om det går att bygga ut funktionaliteten med t.ex. zenity så att det blir användbart även för terminalrädda. Kanske går det på något sätt att knyta skriptet till en tangentbordsgenväg så att det söker på det som för tillfället finns i clipboard (tänk: markera ord, tryck kombination och få en zenityruta med synonymer).

Nåväl, här är vad jag har åstadkommit så långt:

Kod: Markera allt

#!/bin/bash

syn ()
{
 lynx -dump \
 "http://www.synonymer.se/?query=${conv}&btnS=Hitta+synonymer" \
 | sed -e :a -e '$d;N;2,24ba' -e 'P;D' | grep ${1} -A 10 \
 > /tmp/tempsyn.txt
 
         if [[ -s  /tmp/tempsyn.txt ]] ;then
            until ! read response
               do
               echo "${response}"
               done < /tmp/tempsyn.txt
            else
               echo "Ledsen $USER, kan inte hitta \"${1} \""
         fi
rm -f /tmp/tempsyn.txt
}
conv=`echo $1 | iconv -f UTF8 -t ISO_8859-1`
syn $conv
edit: Skriptet kräver, som det ser ut nu, att lynx är installerat.

Re: Bashskript för synonymordlista

Postat: 22 maj 2009, 20:56
av Xappe
Hittade det lilla verktyget xclip. Det verkar funka att använda för urklippsfunktionaliteten, d.v.s. markera ett ord och kör skriptet så söker den på markeringen. Inte testat fullt ut, men det verkar funka:

synoclip.sh

Kod: Markera allt

#!/bin/sh
# Skapare: Xappe, 2009
# Synoclip, ett verktyg för att söka efter svenska synonymer på
# http://www.synonymer.se
# Användning: Markera ett ord och kör skriptet.
# Beroenden: xclip, lynx

syn ()
{
 lynx -dump \
 "http://www.synonymer.se/?query=${conv}&btnS=Hitta+synonymer" \
 | sed -e :a -e '$d;N;2,24ba' -e 'P;D' | grep ${conv} -A 10 \
 > /tmp/tempsyn.txt
 
         if [[ -s  /tmp/tempsyn.txt ]] ;then
            until ! read response
               do
               echo "${response}"
               done < /tmp/tempsyn.txt
            else
              echo "Ledsen $USER, kan inte hitta \"${conv}\"" 
         fi
rm -f /tmp/tempsyn.txt
}
ord=`xclip -o`
conv=`echo $ord | tr ’A-ZÅÄÖ’ ’a-zåäö’ | iconv -t ISO_8859-1`
syn $conv
Edit: Skriptet kräver nu lynx och xclip.
Edit2: Uppdaterat skriptet.

Re: Bashskript för synonymordlista

Postat: 22 maj 2009, 22:37
av Xappe
Jahapps, så fick man en bugg att lösa oxå... :)

tr, som jag lade till för att ändra alla versaler till gemener innan sökning, verkar plocka bort bokstäver när två lika står efter varandra. Markerar man "inlägg" här på sidan och kör skriptet söker den på "inläg". Ett g för lite alltså.

Idéer?

EDIT: Ett -s hade visst smugit sig in. Nu fungerar det.

Re: Bashskript för synonymordlista

Postat: 23 maj 2009, 00:34
av Xappe
Nu anser jag mig klar tills vidare. Det fungerar och passar bra att koppla "urxvt -e synoclip.sh" till en tangentbordskombination.

Det slutgiltiga resultatet:

synoclip.sh

Kod: Markera allt

#!/bin/sh
# Skapare: Xappe, 2009
# Synoclip, ett verktyg för att söka efter svenska synonymer på
# http://www.synonymer.se
# Användning: Markera ett ord och kör skriptet.
# Beroenden: xclip, lynx
syn ()
{
 tempfil=$$.tmp
 lynx -dump \
 "http://www.synonymer.se/?query=${conv}&btnS=Hitta+synonymer" \
 | sed -e :a -e '$d;N;2,24ba' -e 'P;D' | grep ${conv} -A 10 \
 > /tmp/$tempfil
 
         if [[ -s  /tmp/$tempfil ]] ;then
            until ! read response
               do
               echo "${response}"
               done < /tmp/$tempfil
            else
              echo "Ledsen $USER, kan inte hitta \"${conv}\"" 
         fi
rm -f /tmp/$tempfil
}
ord=`xclip -o`
conv=`echo $ord | tr ’A-ZÅÄÖ’ ’a-zåäö’ | iconv -t ISO_8859-1`
syn $conv |iconv -f ISO_8859-1 -t UTF-8 |less

EDIT: Gjorde skriptet lite mer tolerant. Nu borde flera användare kunna köra det samtidigt utan att det får spel.

Re: Bashskript för synonymordlista

Postat: 23 maj 2009, 20:13
av David Andersson
Åäö funkade inte bra för mej. Dels ska de skrivas %F6 etc i url:en, dels returnerade xclip inte alltid text med min default encoding. Jag skrev ett perl-script som gör ungefär samma sak, fast med färger.

Spara som ~/bin/mysynonym

Kod: Markera allt

#!/usr/bin/perl
#
# Söka efter svenska synonymer på http://www.synonymer.se
# Beroenden: perl, xclip, wget
use warnings;
use strict;
use Getopt::Long;
use Text::Iconv;

my $usage = "Användning: mysynonym [FLAGGOR] [ORD]
  --clip   Hämta ord från klippbordet
  --help   Visa denna text";

# Analysera flaggor
my $clip = 0;
GetOptions("clip"  => \$clip,
           "help"  => sub{ die "$usage\n"; }
           ) or exit 1; # felaktig flagga

# Hämta ett ord från kommandoraden (@ARGV) eller klippbordet (xclip -o)
my $word;
if ($clip) {
    open(FH, "xclip -o |") or die $!;
    $word = <FH>;
    close(FH);
    die "Orimlig text i klippbordet\n"  if length($word)>40;
} else {
    $word = shift(@ARGV);
}

# Ska bara vara ett argument på kommandoraden (eller inget om --clip)
die "Fel antal argument\n"  if $#ARGV != -1 || not $word;

# Mappa html-taggar till färgkoder
my $blue   = "\e[34;1m";
my $lila   = "\e[35m";
my $normal = "\e[0m";

my %tagmap=("i"     => $blue,     # fras
	    "/i"    => $normal,
	    "b"     => $blue,     # alternativ-siffra
	    "/b"    => $normal,
	    "font"  => $lila,     # grammatisk klass
	    "/font" => $normal,
	    );

# Konverterare till och från latin1 (www.synonymer.se antas vara latin1)
# Kodning "" borde betyda användarens eller systemets aktuella kodning
# (Konverterarna skapas här men används senare)
my $tolatin1   = Text::Iconv->new("", "ISO-8859-1");
my $fromlatin1 = Text::Iconv->new("ISO-8859-1", "");

# Konvertera åäö till latin1 i $word2, behåll originalet $word
my $word2 = $tolatin1->convert($word) || $word;

# Konvertara åäö och mellanslag etc till %HEX-koder för url:en
$word2 =~ s/([^\w])/sprintf("%%%X",ord($1))/ge;

# Anropa synonym-tjänsten. Pipa svaret i filhandtaget FH
my $url = "http://www.synonymer.se/?query=$word2";
open(FH, "wget --quiet -O- '$url' |") || die $!;

# Analysera html-svaret. Dela upp i tabell-celler.
$/ = "<TD";
while (<FH>) {
    # Om inga hittas står nånstans "0 synonymer hittades för din sökning."
    if (/0 synonymer hittades/) {
	die "Inga synonymer hittades: $word\n";
    }
    # Om nåt hittades finns svaren i html-element märkta
    # med attribut class="ord" för uppslagsord och class="synonym" för synonym
    if (/class="ord">(.*?)</i) {
	my $def = $fromlatin1->convert($1);
	print "$blue$def$normal: ";
    }
    if (/class="synonym">(.*)<\/tr/im) {
	my $def = $fromlatin1->convert($1);
	# Konvertera bold, italic, font och ta bort all annan html-formattering
	$def =~ s/<(\/?\w+).*?>/($tagmap{lc($1)}||"")/eg;
	print "$def\n";
    }
}

close(FH);
Xappe skrev: När allt fungerar tillfredsställande funderar jag på om det går att bygga ut funktionaliteten med t.ex. zenity så att det blir användbart även för terminalrädda. Kanske går det på något sätt att knyta skriptet till en tangentbordsgenväg så att det söker på det som för tillfället finns i clipboard (tänk: markera ord, tryck kombination och få en zenityruta med synonymer).
Jättebra idé. Om man ska visa <b> 1 </b> som [1] i zenity så krävs det lite mer parsning. Mellanslag behöver flyttas runt i resultatet från sajten.

Re: Bashskript för synonymordlista

Postat: 23 maj 2009, 21:00
av Xappe
hmm, åäö funkar utan problem för mig vad jag märkt hittills iaf. Efter att jag lade till konverteringen...

Re: Bashskript för synonymordlista

Postat: 23 maj 2009, 21:50
av Xappe
David Andersson skrev:Jag skrev ett perl-script som gör ungefär samma sak, fast med färger.
Btw, jag tänkte i liknande banor som du innan jag satte igång, d.v.s. formattering av resultatet. Men det fallerade på min knapphändiga hantering av reguljära uttryck.

Känner du att du har tid till övers får du gärna gå igenom de reguljära uttrycken i lite mer detalj. Såvida det inte är helt perlspecifikt vill säga. :)

Re: Bashskript för synonymordlista

Postat: 24 maj 2009, 00:55
av David Andersson
Xappe skrev: Känner du att du har tid till övers får du gärna gå igenom de reguljära uttrycken i lite mer detalj. Såvida det inte är helt perlspecifikt vill säga. :)
Bakgrund: Det finns olika syntax för reguljära uttryck (regex). Det man vanligen menar är unix-regex, men det finns andra syntaxer i t.ex yacc och omnimark. Även unix-regex är inte exakt samma överallt. Den i grep är mer basic och inte kompatibel med den i egrep som i sin tur inte är helt kompatibel med den i emacs. Regex i perl, java och python är alla utökade regex och rätt så lika. Är inte säker men jag tror att regex i awk är ganska nära den i egrep och regex i sed är mer lik den i grep fast mer kraftfull än den i awk.

Förutom regex:arna så kan lite av grekiskan runtomkring behöva förklaras. Det blir väldigt perl-specifikt.

Förklaringar

$word2 =~ s/aaa/bbb/g perl-uttryck för att i variabel $word2 byta ut alla "aaa" till "bbb". Vänsterledet (aaa) är regex och högerledet (bbb) är bara en sträng. "g"(=global) för att byta alla förekomster av "aaa", inte bara första.

Rad 57: $word2 =~ s/([^\w])/sprintf("%%%X",ord($1))/ge;

"\w" (word constituent) matchar ett alfanumeriskt tecken.
"^" efter "[" är invers. "[^\w]" matchar alla tecken utom alfanumeriska, ett tecken.
"(" och ")" bildar en grupp och gör att texten som matchar sparas i "$1".
Det sista "e" (evaluate) betyder att högerledet inte är en sträng utan ett uttryck som ska evalueras och som returnerar en sträng. Här körs sprintf "%%%H" för att formatera ett hex-tal. "%%" blir "%" och "%H" blir t.ex "F6". "ord" tar ascii-värdet av "$1", tecknet som matchade.

m/regex/ kan även skrivas /regex/ och betyder sök efter regex i variabel $_. (m=match)

Rad 67: if (/0 synonymer hittades/) {

Variabel $_ är automatisk loop-variabel i while(<FH>). Testar om angiven text finns i senast inlästa block från filen.

Rad 72: if (/class="ord">(.*?)</i) {

Letar i $_ efter ett avsnitt som börjar med "class="ord">" och slutar med "<". Mellanledet ".*?" matchar 0 eller flera vadsomhelst. Skillnaden med ".*" är att ".*?" försöker matcha så lite som möjligt, så efterföljande "<" är den första "<" som matchar. "(" ")" sparar texten som matchar ".*?" i variabeln "$1". "i" i slutet betyder ignore case.

Rad 76: if (/class="synonym">(.*)<\/tr/im) {

Ungefär som ovan. Matchningen slutar vid första förekomsten av "</tr". "m" (multiple line) kanske är ett feltänk från min sida. Kanske ska vara "s" (single line).

Rad 79: $def =~ s/<(\/?\w+).*?>/($tagmap{lc($1)}||"")/eg;

Sök i variabel $def efter en html-tag "<...>". "\/" matchar "/". "?" betyder 0 eller 1 antal. Altså 0 eller 1 "/". "\w+" är 1 eller flera alfanumeriska. ".*?" är 0 eller flera vadsomhelst. "(" ")" sparar de alfanumeriska med ev inledande "/" i variabel "$1". "e" gör att högerledet evaluaras. Om det är en träff på en tagg, slå upp $1 i hashtabell "tagmap". Om det inte blev träff i hashtabellen använd alternativet efter "||", tom sträng. "lc" gör om till lower case, så både "B" och "b" känns igen som html-tagg för bold. Sista "g" (global) för att gå igenom hela $def och inte stanna efter första träffen.

Enkelt va?

Re: Bashskript för synonymordlista

Postat: 24 maj 2009, 12:54
av Xappe
Tack för genomgången!
Tror jag måste ta och läsa e-boken om reguljära uttryck jag har nånstans. Måste bara leta upp den först. :)