[bash] String manipulatie zonder externe applicaties

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • highking
  • Registratie: Augustus 2001
  • Laatst online: 11-02-2022

highking

Langharig tuig

Topicstarter
Puur in een poging iets te leren wil ik graag een bash-script schrijven waarin ik zo min mogelijk aan extern aan te roepen applicaties over wil laten.

In de variabele "file" heb ik het volledig pad naar een bestand staan, bijvoorbeeld "/plugin/dir/naam.plugin".
Nu wil ik enkel "naam" weergeven. Dit kan ik bijvoorbeeld met basename doen, maar liefst roep ik die niet aan.

Op deze manier krijg ik naam.plugin:
code:
1
echo ${file##*/}


En hiermee krijg ik /plugin/dir/naam:
code:
1
echo ${file%*.plugin}


Is het mogelijk deze 2 samen te voegen? Natuurlijk kan ik eerst de bovenste in een variabele steken en die vervolgens door de onderste halen, dan ben ik er ook... maar als het in 1 slag zou kunnen zou dat natuurlijk helemaal te gek zijn. :)

In de Advanced Bash-Scripting Guide kan ik bovenstaande wel vinden, maar niet of ze ook samen te gebruiken zijn.
Uiteraard heb ik wel wat pogingen gedaan, bijvoorbeeld ${file##*/%*.plug} maar nie werken nie.

Acties:
  • 0 Henk 'm!

  • Raynman
  • Registratie: Augustus 2004
  • Nu online
highking schreef op woensdag 20 juni 2012 @ 14:59:
Uiteraard heb ik wel wat pogingen gedaan, bijvoorbeeld ${file##*/%*.plug} maar nie werken nie.
Ik zou eerder aan geneste substituties denken, maar dan moet je waarschijnlijk upgraden naar zsh, zie http://stackoverflow.com/...shell-parameter-expansion. (resultaten van snelle test met beide shells komen overeen met de antwoorden daar)

[ Voor 9% gewijzigd door Raynman op 20-06-2012 16:18 ]


Acties:
  • 0 Henk 'm!

  • lazydave-II
  • Registratie: September 2005
  • Laatst online: 09-01 18:00
stringZ=/plugin/dir/naam.plugin
echo ${stringZ:12:4}

nu zonder gekheid: nee... lang antwoord: nee

Acties:
  • 0 Henk 'm!

  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

(jarig!)
Helaas, die string substitutions kunnen niet samengevoegd worden. Je moet 't dus in twee stappen doen:

$ foo=/plugin/dir/naam.plugin
$ foo=${foo##*/}; foo=${foo%%.plugin}
$ echo $foo
naam

All my posts are provided as-is. They come with NO WARRANTY at all.


Acties:
  • 0 Henk 'm!

  • ThomVis
  • Registratie: April 2004
  • Laatst online: 11-09 21:04

ThomVis

Detected rambling:

Die laatste zou nog
foo=${foo%%.*}

kunnen zijn.

[ Voor 5% gewijzigd door ThomVis op 20-06-2012 18:36 ]

You don't have to know how the computer works, just how to work the computer.


Acties:
  • 0 Henk 'm!

  • Ertepeller
  • Registratie: November 2010
  • Laatst online: 03-10 10:43
ThomVis schreef op woensdag 20 juni 2012 @ 18:36:
Die laatste zou nog
foo=${foo%%.*}

kunnen zijn.
Dan wel een enkele %. Een dubbele is "greedy" en eet de string van achteraf zo ver mogelijk op. Als er ergens in een directorynaam een "." zit (bv /plugin.lib/dir/naam.plugin) ben je alles achter de voorste "." kwijt. Een enkele % stopt bij de achterste "."

[ Voor 8% gewijzigd door Ertepeller op 20-06-2012 21:09 ]


Acties:
  • 0 Henk 'm!

  • sam.vimes
  • Registratie: Januari 2007
  • Laatst online: 08-06 08:44
Ertepeller schreef op woensdag 20 juni 2012 @ 21:07:
[...]

Dan wel een enkele %. Een dubbele is "greedy" en eet de string van achteraf zo ver mogelijk op. Als er ergens in een directorynaam een "." zit (bv /plugin.lib/dir/naam.plugin) ben je alles achter de voorste "." kwijt. Een enkele % stopt bij de achterste "."
Maar die directory is er een stap eerder net afgehaald. (De volgorde waarin je stukken verwijdert is dus wel belangrijk: eerst de directory eraf, daarna de extensie.)
Maar als er meerdere punten in de naam zouden zitten, zou je met %% inderdaad alleen het voorste stuk overhouden.

Overigens is het performancetechnisch best wel interessant om het gebruik van $(basename ...) te vervangen door de stringfuncties in bash, vooral als je veel bestanden in een lus moet verwerken.
Een kleine test onder Cygwin op mijn laptop (geen snelheidsmonster) spreekt boekdelen:
$ time find | while read f; do basename "$f"; done > /dev/null

real    2m23.874s
user    0m6.649s
sys     0m40.976s

$ time find | while read f; do echo "${f##*/}"; done > /dev/null

real    0m0.651s
user    0m0.123s
sys     0m0.248s


maar dat komt voornamelijk doordat onder Cygwin de 'exec()' system call erg duur is.

Acties:
  • 0 Henk 'm!

  • deadinspace
  • Registratie: Juni 2001
  • Laatst online: 18:29

deadinspace

The what goes where now?

sam.vimes schreef op donderdag 21 juni 2012 @ 08:54:
Een kleine test onder Cygwin op mijn laptop (geen snelheidsmonster) spreekt boekdelen: [...]
Vergeet niet de tests ook eens in tegenovergestelde volgorde te doen, anders kan het zijn dat je bij de eerste benchmark een ongecachede find draait, en bij de tweede een gecachede...

Al zal het verschil groot blijven; fork() + exec() is nou eenmaal duur vergeleken bij wat string manipulaties. Hier, onder bash 4.1.5 is het 2m53s vs 11.5s, en onder zsh 4.3.10 is het 2m53s vs 5.6s (beiden op Debian). Als je echt snelheid nodig hebt is het misschien verstandig naar wat anders dan shell scripts te kijken ;)

Acties:
  • 0 Henk 'm!

  • highking
  • Registratie: Augustus 2001
  • Laatst online: 11-02-2022

highking

Langharig tuig

Topicstarter
Het scheelt inderdaad nogal wat tijd om geen externe binaries aan te hoeven roepen. Dát en het risico een aangepaste binary aan te roepen zijn voor mij de voornaamste redenen om dit te proberen. Verder is het natuurlijk gewoon "voor de fun". :+

Maar jammer dat het niet samen kan. Dan maar even via een variabele. :)

Ik had dit al in m'n script staan:
pluginname=${file##*/}&&pluginname=${pluginname%*.plug}
Dit werkt uitstekend, maar hoe minder hoe beter vandaar de vraag. :)

Acties:
  • 0 Henk 'm!

  • highking
  • Registratie: Augustus 2001
  • Laatst online: 11-02-2022

highking

Langharig tuig

Topicstarter
Iets anders, in hetzelfde straatje: Heeft iemand een idee waarom dit niet werkt zoals verwacht?
$ myuptime=$(</proc/uptime);
$ echo ${myuptime%*.}
8674.60 15021.98
Ik zou verwachten enkel 8674 als ouput te krijgen... maar ik krijg de gehele inhoud van /proc/uptime. Iemand een idee waarom?

Het vreemde is dat het omgekeerde wel werkt:
$ echo ${myuptime#*.}
60 15021.98


edit:

Ik heb het al gevonden, sorry... :)

$ echo ${myuptime%%.*}
8674

[ Voor 9% gewijzigd door highking op 22-06-2012 11:22 ]


Acties:
  • 0 Henk 'm!

  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

(jarig!)
% werkt vanaf het einde, # vanaf het begin.

All my posts are provided as-is. They come with NO WARRANTY at all.

Pagina: 1