Sida 1 av 2
Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 19:54
av Johnny Rosenberg
Har ett litet problem som jag gärna, om möjligt, vill ha en lösning på på ren skriptnivå.
Jag håller på att utveckla ett enkelt skript som jag gjort vid ett tidigare tillfälle. Istället för att fördefiniera allt i variabler vill jag läsa in dem via dialogrutor istället (under förutsättning att användaren vill det, annars vill jag ha variablerna inlästa från en fil, men det blir ett senare problem).
Många av variablerna är sökvägar och jag använder
Xdialog --dselect och
Xdialog --fselect för att låta användaren välja sina sökvägar resprktive filer. När det gäller val av mappar uppstår ett litet dilemma: Efter att ha valt en mapp returneras en sökväg med följande utseende, exempelvis:
I de flesta fall i mitt skript vill ja hellre ha följande form:
Detta gör det möjligt att använda båda varianterna då jag enkelt, vid behov, bara kan infoga ett snedstreck, exempelvis:
Kod: Markera allt
MINMAPP=`Xdialog --stdout --dselect bla bla bla bla`
MINFIL="${MINMAPP}/FinFil.mjölk"
Så min fråga är: Finns det några inbyggda stränghanteringsfunktioner som man kan använda i sh-skript, som motsvarar exempelvis BASIC-funktionerna RIGHT, LEFT, MID och LEN? Då skulle vara enkelt att plocka bort sista tecknet.
Eller måste jag använda sed (som jag för övrigt är väldigt mycket nybörjare på)?
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 20:02
av Lars
Ett enkelt sätt är att bara acceptera att det blir dubbla snedstreck, det fungerar ändå

Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 20:07
av Konservburk
Det går att plocka bort det avslutande / om du nu vill det...
använd "${MINMAPP%/}" istället för "${MINMAPP}".
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 20:42
av mcNisse
Det där är inte bourne shell...
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 21:01
av Konservburk
mcNisse skrev:Det där är inte bourne shell...
Du menar ${VAR%/} ? Jag ser inte att någon efterfrågar att det ska fungera med orinal bourne shell från 70-talet. Den där konstruktionen är giltig posix sh och fungerar med alla moderna varianter av /bin/sh.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 21:06
av mcNisse
Det viste jag inte...
Jag har haft problem med sh på HPUX.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 21:18
av Konservburk
mcNisse skrev:Det viste jag inte...
Jag har haft problem med sh på HPUX.
Har aldrig använt HPUX sh. Det är möjligt att den inte är posix-kompatibel. Jag har för mig att SunOS sh inte heller klarar av det. Den som är nyfiken kan läsa vad ett posix-kompatibelt sh ska klara här:
http://www.opengroup.org/onlinepubs/009 ... hap02.html
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 21:54
av Johnny Rosenberg
Såg att det droppat in en del svar, men jag tar väl ett i taget…
Lars skrev:Ett enkelt sätt är att bara acceptera att det blir dubbla snedstreck, det fungerar ändå

Jo, men i vissa fall vill jag ju ha utan avslutande snedstreck, typ:
Kod: Markera allt
#!/bin/sh
A="${HOME}/Eget"
B="/media/Backup/guraknugen"
C="${HOME}/.LogBackup"
D="${HOME}/.ExcludedFromBackup"
rsync -auv --exclude-from ${D} --delete-during --delete-excluded "${A}" "${B}/" >> "${C}"
Här har jag ju lagt till ett snedstreck för B men inte till de övriga sökvägarna. Jag föredrar att lägga till dem i koden istället för att ha dem i variablerna. Det gör det lättare att hålla sig konsekvent. Då vet jag att sökvägarna ALDRIG slutar med snedstreck och kan bara pytsa dit dem där det behövs.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 21:59
av Johnny Rosenberg
Konservburk skrev:Det går att plocka bort det avslutande / om du nu vill det...
använd "${MINMAPP%/}" istället för "${MINMAPP}".
Det där ser ju smidigt ut. Kan du förklara hur man ska tänka? Vad hade du gjort om du velat ta bort de 5 siste tecknen, exempelvis? Eller om du hade velat ta bort sista steget i sökvägen?
Eller med andra ord:
Kod: Markera allt
/min/intressanta/sökväg/ → /min/intressanta/sö # 5 sista tecknen borttagna
/min/intressanta/sökväg/ → /min/intressanta # sista nivån borttagen samt sista snedstrecket
Varför jag frågar är för att det bär mig emot att ha med saker i min kod om jag inte vet hur det fungerar. Annars blir det lätt så att man råkar på ett liknande problem, men eftersom man inte vet hur den förra lösningen fungerar så kan man inte modifiera den till den nya situationen.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 26 jan 2009, 22:19
av Johnny Rosenberg
Johnny Rosenberg skrev:Eller med andra ord:
Kod: Markera allt
/min/intressanta/sökväg/ → /min/intressanta/sö # 5 sista tecknen borttagna
/min/intressanta/sökväg/ → /min/intressanta # sista nivån borttagen samt sista snedstrecket
Varför jag frågar är för att det bär mig emot att ha med saker i min kod om jag inte vet hur det fungerar. Annars blir det lätt så att man råkar på ett liknande problem, men eftersom man inte vet hur den förra lösningen fungerar så kan man inte modifiera den till den nya situationen.
Den senare kom jag på:
Verkar fungera i alla fall, sedan om det är rätt är kanske en annan sak…
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 27 jan 2009, 01:15
av Konservburk
Johnny Rosenberg skrev:Konservburk skrev:Det går att plocka bort det avslutande / om du nu vill det...
använd "${MINMAPP%/}" istället för "${MINMAPP}".
Det där ser ju smidigt ut. Kan du förklara hur man ska tänka? Vad hade du gjort om du velat ta bort de 5 siste tecknen, exempelvis? Eller om du hade velat ta bort sista steget i sökvägen?
Eller med andra ord:
Kod: Markera allt
/min/intressanta/sökväg/ → /min/intressanta/sö # 5 sista tecknen borttagna
/min/intressanta/sökväg/ → /min/intressanta # sista nivån borttagen samt sista snedstrecket
Varför jag frågar är för att det bär mig emot att ha med saker i min kod om jag inte vet hur det fungerar. Annars blir det lätt så att man råkar på ett liknande problem, men eftersom man inte vet hur den förra lösningen fungerar så kan man inte modifiera den till den nya situationen.
De fem sista tecknen skulle jag tagit bort så här:
"${A%?????}", dvs fem stycken ? efter varandra. Detta eftersom ? matchar ett enda tecken, vilket som helst. Att ta bort "sista steget" går att göra precis som du redan listat ut med
"${A%/*/}", förutsatt att det finns ett och endast ett / på slutet.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 27 jan 2009, 18:06
av Johnny Rosenberg
Konservburk skrev:Johnny Rosenberg skrev:Konservburk skrev:Det går att plocka bort det avslutande / om du nu vill det...
använd "${MINMAPP%/}" istället för "${MINMAPP}".
Det där ser ju smidigt ut. Kan du förklara hur man ska tänka? Vad hade du gjort om du velat ta bort de 5 siste tecknen, exempelvis? Eller om du hade velat ta bort sista steget i sökvägen?
Eller med andra ord:
Kod: Markera allt
/min/intressanta/sökväg/ → /min/intressanta/sö # 5 sista tecknen borttagna
/min/intressanta/sökväg/ → /min/intressanta # sista nivån borttagen samt sista snedstrecket
Varför jag frågar är för att det bär mig emot att ha med saker i min kod om jag inte vet hur det fungerar. Annars blir det lätt så att man råkar på ett liknande problem, men eftersom man inte vet hur den förra lösningen fungerar så kan man inte modifiera den till den nya situationen.
De fem sista tecknen skulle jag tagit bort så här:
"${A%?????}", dvs fem stycken ? efter varandra. Detta eftersom ? matchar ett enda tecken, vilket som helst. Att ta bort "sista steget" går att göra precis som du redan listat ut med
"${A%/*/}", förutsatt att det finns ett och endast ett / på slutet.
Aha, jag försökte med ”.” som i en del sammanhang brukar betyda ”ett valfritt tecken”, men det gick givetvis inte. Märkte också att om man skriver en teckenkombination som inte stämmer med hur strängen ser ut, så struntar den bara i att göra något…
Verkar som att % betyder ”slutet av strängen” och jag läste mig just till att # är dess motsats, samt att man även kan använda %% och ##. Tyckte det kändes väldigt primitivt när jag läste om det, men man kanske kan göra mer än man tror med dem.
Finns det också sätt att ta bort tecken mitt i textsträngen eller tvärt om, eller blir man tvungen att blanda in sed då?
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 27 jan 2009, 18:31
av Konservburk
Johnny Rosenberg skrev:Verkar som att % betyder ”slutet av strängen” och jag läste mig just till att # är dess motsats, samt att man även kan använda %% och ##. Tyckte det kändes väldigt primitivt när jag läste om det, men man kanske kan göra mer än man tror med dem.
Det går att göra mycket mer än man först tror, speciellt när man börjar kombinera dem.
Johnny Rosenberg skrev:Finns det också sätt att ta bort tecken mitt i textsträngen eller tvärt om, eller blir man tvungen att blanda in sed då?
Du kan använda # % ## %% upprepade gånger. Sedan har bash även
"${VAR/a/b}" och
"${VAR//a/b}" för att ersätta mitt i, men då är det ju inte sh längre.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 27 jan 2009, 22:25
av Johnny Rosenberg
Konservburk skrev:Johnny Rosenberg skrev:Finns det också sätt att ta bort tecken mitt i textsträngen eller tvärt om, eller blir man tvungen att blanda in sed då?
Du kan använda # % ## %% upprepade gånger. Sedan har bash även
"${VAR/a/b}" och
"${VAR//a/b}" för att ersätta mitt i, men då är det ju inte sh längre.
Kanske är dumt att begränsa sig till sh, i och för sig, när bash verkar bättre, i alla fall när det gäller de skillnader jag sett. Man måste ju inte vara kompatibel med allt…
Det vanliga sh verkar ju också oerhört bökigt när det gäller att göra enkla matematiska beräkningar, då man tydligen måste blanda in externa program för att få jobbet gjort, exempelvis expr. Extra komiskt blir det när det visar sig att ett uttryck som ”expr 2 * 3” inte är giltigt, utan man istället måste skriva ”expr 2 \* 3”. Mycket sådant och koden blir ganska svårläst… Har fått för mig att enkla beräkningar låter sig göras mycket enklare och snyggare i bash, men jag är inte så påläst när det gäller bash. På den gamla goda tiden (slutet av 1980-talet) när man larvade sig lite i Unix så var det tcsh och csh som gällde där jag befann mig, men förmodligen är bash bättre.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 27 jan 2009, 22:42
av Konservburk
Johnny Rosenberg skrev:Kanske är dumt att begränsa sig till sh, i och för sig, när bash verkar bättre, i alla fall när det gäller de skillnader jag sett. Man måste ju inte vara kompatibel med allt…
Det vanliga sh verkar ju också oerhört bökigt när det gäller att göra enkla matematiska beräkningar, då man tydligen måste blanda in externa program för att få jobbet gjort, exempelvis expr. Extra komiskt blir det när det visar sig att ett uttryck som ”expr 2 * 3” inte är giltigt, utan man istället måste skriva ”expr 2 \* 3”. Mycket sådant och koden blir ganska svårläst… Har fått för mig att enkla beräkningar låter sig göras mycket enklare och snyggare i bash, men jag är inte så påläst när det gäller bash.
Du kan göra enkla beräkningar även med
sh utan att blanda in
expr alls:
Då pratar jag åter igen om posix sh, till skillnad från antika varianter. Jag påminner även om länken som tar upp syntaxen:
http://www.opengroup.org/onlinepubs/009 ... hap02.html
Johnny Rosenberg skrev:På den gamla goda tiden (slutet av 1980-talet) när man larvade sig lite i Unix så var det tcsh och csh som gällde där jag befann mig, men förmodligen är bash bättre.
Antar att det var BSD-grenen av Unix till skillnad från systemV-grenen.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 28 jan 2009, 19:23
av Johnny Rosenberg
Konservburk skrev:Johnny Rosenberg skrev:På den gamla goda tiden (slutet av 1980-talet) när man larvade sig lite i Unix så var det tcsh och csh som gällde där jag befann mig, men förmodligen är bash bättre.
Antar att det var BSD-grenen av Unix till skillnad från systemV-grenen.
Jag var mer fokuserad på skolarbetet på den tiden, men som jag fattade det så var det båda, om nu det är möjligt. Om inte, så var det Berkeley.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 30 jan 2009, 21:20
av David Andersson
Johnny Rosenberg skrev: Eller om du hade velat ta bort sista steget i sökvägen?
Eller med andra ord:
Kod: Markera allt
/min/intressanta/sökväg/ → /min/intressanta/sö # 5 sista tecknen borttagna
/min/intressanta/sökväg/ → /min/intressanta # sista nivån borttagen samt sista snedstrecket
Ta ut delsträngar
Förutom att plocka bort saker i parameterexpansion kan man använda nån av kommandona
basename,
dirname och
expr .
Exempel
Kod: Markera allt
dirname /a/b/c
basename /a/b/c
expr "abc47def" : "[^0-9]*\([0-9]*\)"
dir=$(dirname "$mypath")
base=$(basename "$mypath")
digits=$(expr "$mystring" : "[^0-9]*\([0-9]*\)")
(Basename och dirname är gjorda så att när man plockat isär en path med dem så kan man sätta ihop delarna med / emellan och få tillbaka samma path. Inte alltid samma sträng, men en path som går till samma ställe. Den förstår dubbla // och sånt.)
Testa på delsträngar
Om man inte behöver en delsträng explicit utan bara ska göra en test av förekomsten av en delsträng kan man använda
[[ ... =~ ...]],
expr, och inte att glömma
case.
Exempel
Kod: Markera allt
expr "abba" : ".*bb" # 3, abba innehåller bb
expr "abba" : ".*cc" # 0, innehåller inte cc
if expr "$mystring" : ".*buntu"; then echo "Yeah!"; fi
if [[ "$mystring" =~ "buntu" ]]; then echo "Yeah!"; fi
case "$mystring" in
*buntu*) echo "Yeah!";;
*indow*) echo "Yack!";;
*) echo "Åh, vet inte";;
esac
I expr finns vanlig tråkig
substr, men den använder man aldrig. Påminner alldeles för myckt om Basic och Java.
Exempel
(Edit: [[ ... ]] finns inte i sh)
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 31 jan 2009, 16:03
av Johnny Rosenberg
David Andersson skrev:Ta ut delsträngar
Förutom att plocka bort saker i parameterexpansion kan man använda nån av kommandona
basename,
dirname och
expr .
Exempel
Kod: Markera allt
dirname /a/b/c
basename /a/b/c
expr "abc47def" : "[^0-9]*\([0-9]*\)"
dir=$(dirname "$mypath")
base=$(basename "$mypath")
digits=$(expr "$mystring" : "[^0-9]*\([0-9]*\)")
(Basename och dirname är gjorda så att när man plockat isär en path med dem så kan man sätta ihop delarna med / emellan och få tillbaka samma path. Inte alltid samma sträng, men en path som går till samma ställe. Den förstår dubbla // och sånt.)
Det lustiga är att jag själv redan tittat på både dirname och basename, men dömde ut dem efter några snabba tester, för de gjorde inte det jag ville. Men nu när du åter tar upp det hela gjorde jag flera tester och fann att de visst gör det jag vill, om man bara tänker till lite.
Vad jag har som utgångspunkt är ju följande:
Vad jag vill ha är detta;
Den enkla sats som fixar detta ser givetvis ut så här, om sökvägen finns i variabeln ”a”:
Alternativ:
En liten följdfråga bara, när det gäller skalprogrammering (sh) i allmänhet:
Kan man alltid ersätta `<uttryck>` med $(<uttryck>) eller är detta bara ett specialfall? Båda varianterna fungerade i alla fall, men ` sitter lite avigt till på mitt tangentbord, trots att jag gjort min egen layout. Använder jag $() istället slipper jag ju göra om min tangentbordslayout igen…
David Andersson skrev:I expr finns vanlig tråkig
substr, men den använder man aldrig. Påminner alldeles för myckt om Basic och Java.
Exempel
(Edit: [[ ... ]] finns inte i sh)
Fast å andra sidan, om något är tråkigt eller inte spelar ju mindre roll om enda målet är att få jobbet gjort. Jag har lättare att köpa argument som de mer handlar om prestanda och dylikt. När jag själv hittar flera olika alternativ och ska välja ett brukar jag ta tid på dem. Skiljer det mycket tar jag det snabbaste. Skiljer det lite kanske jag väljer det som ser mest lättläst ut eller som slukar minst resurser eller det som bäst matchar koden i övrigt. Men snabbhet är prio 0 för mig. Eller prio 1 kanske man brukar säga.
I just detta fall visade sig båda vara lika snabba. Ibland var den ena lite snabbare, ibland den andra. Här är en av testerna:
Kod: Markera allt
$ a="/home/guraknugen/Eget/"
$ time for i in $(seq 1 1000); do b="$(dirname $a)/$(basename $a)"; done
real 0m9.523s
user 0m4.392s
sys 0m5.324s
$ time for i in $(seq 1 1000); do b="`dirname $a`/`basename $a`"; done
real 0m9.585s
user 0m4.292s
sys 0m5.276s
$ echo $b
/home/guraknugen/Eget
I stort sett samma tid tog det, som synes. Det blir som sagt lite olika varje gång.
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 31 jan 2009, 16:46
av Konservburk
Johnny Rosenberg skrev:En liten följdfråga bara, när det gäller skalprogrammering (sh) i allmänhet:
Kan man alltid ersätta `<uttryck>` med $(<uttryck>) eller är detta bara ett specialfall? Båda varianterna fungerade i alla fall, men ` sitter lite avigt till på mitt tangentbord, trots att jag gjort min egen layout. Använder jag $() istället slipper jag ju göra om min tangentbordslayout igen…
$() och `` är i princip identiska. Det skiljer en del när de gäller nästling, men det är inget du behöver bry dig om du inte ska ha väldigt mycket kod innuti dem (t.ex. hela case-satser).
Annars är den stora skillnaden att `` fungerar med alla sh som finns (inklusive orginalet från 70-talet), medan $() bara fungerar med posix-kompatibla sh. Jag brukar hur som hest hålla mig till $() eftersom den är mer lättläst.
Johnny Rosenberg skrev:Fast å andra sidan, om något är tråkigt eller inte spelar ju mindre roll om enda målet är att få jobbet gjort. Jag har lättare att köpa argument som de mer handlar om prestanda och dylikt. När jag själv hittar flera olika alternativ och ska välja ett brukar jag ta tid på dem. Skiljer det mycket tar jag det snabbaste. Skiljer det lite kanske jag väljer det som ser mest lättläst ut eller som slukar minst resurser eller det som bäst matchar koden i övrigt. Men snabbhet är prio 0 för mig. Eller prio 1 kanske man brukar säga.
I just detta fall visade sig båda vara lika snabba. Ibland var den ena lite snabbare, ibland den andra. Här är en av testerna:
Kod: Markera allt
$ a="/home/guraknugen/Eget/"
$ time for i in $(seq 1 1000); do b="$(dirname $a)/$(basename $a)"; done
real 0m9.523s
user 0m4.392s
sys 0m5.324s
$ time for i in $(seq 1 1000); do b="`dirname $a`/`basename $a`"; done
real 0m9.585s
user 0m4.292s
sys 0m5.276s
$ echo $b
/home/guraknugen/Eget
I stort sett samma tid tog det, som synes. Det blir som sagt lite olika varje gång.
Det som tar upp den mesta tiden där är fork:andet av
dirname och
basename. Använder du istället
b="${a%/}" så lär det gå betydligt snabbare. Å andra sidan fungerar det då bara om det inte finns fler än ett / på slutet. Är man lite kreativ går den biten också att komma runt, iofs med risk för att det blir onödigt svårläst.
Kod: Markera allt
$ time for i in $(seq 1 1000); do b="$(dirname $a)/$(basename $a)"; done
real 0m1.787s
user 0m0.423s
sys 0m1.353s
$ time for i in $(seq 1 1000); do b="${a%/}"; done
real 0m0.018s
user 0m0.013s
sys 0m0.000s
Re: Stränghantering i sh (eller i nödfall i bash)?
Postat: 31 jan 2009, 22:45
av Johnny Rosenberg
Konservburk skrev:Det som tar upp den mesta tiden där är fork:andet av
dirname och
basename. Använder du istället
b="${a%/}" så lär det gå betydligt snabbare. Å andra sidan fungerar det då bara om det inte finns fler än ett / på slutet. Är man lite kreativ går den biten också att komma runt, iofs med risk för att det blir onödigt svårläst.
Kod: Markera allt
$ time for i in $(seq 1 1000); do b="$(dirname $a)/$(basename $a)"; done
real 0m1.787s
user 0m0.423s
sys 0m1.353s
$ time for i in $(seq 1 1000); do b="${a%/}"; done
real 0m0.018s
user 0m0.013s
sys 0m0.000s
Din maskin verkar vara drygt 5 gånger så snabb som min, för första exemplet är väl exakt samma som mitt? Bara det var ju imponerande. Nu är min bärbara kanske inte så där jätteimponerande: Dubbelkärna, 1,6 GHz, 2 GiB minne.
"${a%/}" kommer att fungera alldeles utmärkt i mitt fall, eftersom originalsträngen kommer ifrån
Xdialog --dselect som ju alltid returnerar en sökväg med exakt en ”/” på slutet.