[gelöst] bash: Substring entfernen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
k2
Beiträge: 132
Registriert: 14.08.2002 22:29:28
Lizenz eigener Beiträge: MIT Lizenz

[gelöst] bash: Substring entfernen

Beitrag von k2 » 08.12.2015 12:34:19

Hi,

in einer Funktion wird ein Verzeichniseintrag (übergeben mittels $*) getestet auf:
- ist der Eintrag ein Verzeichnis?
- und entspricht er dem Muster: [0-9][0-9][0-9][0-9]_--_*.indexhtml oder [0-9][0-9][0-9][0-9]_--_*.indexhtm ?
Danach soll der Variable html_htm je nach Verzeichnisendung html bzw. htm übergeben werden.

Das Skript:

Code: Alles auswählen

if   [ -d "$*" ] &&                                         \
   ( [[ "$*" == [0-9][0-9][0-9][0-9]_--_*.indexhtml ]] ||   \
     [[ "$*" == [0-9][0-9][0-9][0-9]_--_*.indexhtm ]] )
then
   html_htm=${*##[0-9][0-9][0-9][0-9]_--_*.index}
   echo "html_htm: "$html_htm
   # und weiter gehts...
fi
Das Problem: Sind in dem Verzeichnisnamen Leerzeichen enthalten funktioniert ${*##[0-9][0-9][0-9][0-9]_--_*.index} nicht mehr, es wird der gesamte String anstatt htm oder html übergeben.

Meine Frage: Wie kann ich das quoten?
html_htm=${"*"##[0-9][0-9][0-9][0-9]_--_*.index} ergibt: Falsche Variablenersetzung.

Umformulierung funktioniert auch nicht
html_htm=${*\[0-9][0-9][0-9][0-9]_--_*.index} ergibt: Falsche Variablenersetzung.
html_htm=${*#*.index} gibt 1. Teil vor Leerzeichen und html bzw htm aus

Welche Möglichkeiten habe ich noch außer die Variablenzuweisung in die if-Abfrage einzubauen?

Vielen Dank vorab für Eure Mühe
k2
Zuletzt geändert von k2 am 08.12.2015 15:54:44, insgesamt 1-mal geändert.

Benutzeravatar
Meillo
Moderator
Beiträge: 9235
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: bash: Substring entfernen

Beitrag von Meillo » 08.12.2015 14:00:36

k2 hat geschrieben: Das Problem: Sind in dem Verzeichnisnamen Leerzeichen enthalten funktioniert ${*##[0-9][0-9][0-9][0-9]_--_*.index} nicht mehr, es wird der gesamte
String anstatt htm oder html übergeben.
Ich kann das Problem leider nicht nachvollziehen:

Code: Alles auswählen

$ a="irgendwas.indexhtm"
$ b=${a##*.index}       
$ echo $b
htm

$ a="irgendwas mit spaces.indexhtm"
$ b=${a##*.index}                  
$ echo $b                          
htm
Bitte poste Beispieleingaben mit denen man das Problem nachstellen kann. Beim Code solltest du zuerst schauen, ob das Problem bei der If-Bedingung (die ich ganz schoen komplex finde) oder bei der Variablensubstitution liegt. Dann konstruiere ein Minimalcodebeispiel, als Shellscript/-funktion, mit der wir testen koennen.
Meine Frage: Wie kann ich das quoten?
Die Frage ist, ob es daran liegt, aber wenn, dann musst du den ganzen Variablensubstitutionsausdruck quoten, so:

Code: Alles auswählen

html_htm="${*##[0-9][0-9][0-9][0-9]_--_*.index}"
Use ed once in a while!

k2
Beiträge: 132
Registriert: 14.08.2002 22:29:28
Lizenz eigener Beiträge: MIT Lizenz

Re: bash: Substring entfernen

Beitrag von k2 » 08.12.2015 15:53:34

Hi,

das Problem, oder nennen wir es die Eigenart, liegt definitiv bei der Variablensubtitution mit $* wenn $* Leerzeichen enthält. Ich habe jetzt zwei Lösungen gefunden:
1. den Inhalt von $* in eine Hilfsvariable leiten und diese substituieren, oder
2. beim Aufruf der Funktion den Funktionsparameter quoten: funktion "$var"
html_htm="${*##[0-9][0-9][0-9][0-9]_--_*.index}" funktioniert leider nicht.

Mit folgendem Skript kann man es nachvollziehen:

Code: Alles auswählen

#!/bin/bash
html_htm=
var=
verzeichnisname="0123_--_Das ist ein Verzeichnis.indexhtml"

eine_funktion() {
     html_htm=${*##[0-9][0-9][0-9][0-9]_--_*.index}
     echo "html_htm: "$html_htm
echo
     var=$*
     html_htm=${var##[0-9][0-9][0-9][0-9]_--_*.index}
     echo "mit Hilfsvariable var: "$var
     echo "html_htm: "$html_htm
echo
     echo 'html_htm="${*##[0-9][0-9][0-9][0-9]_--_*.index}"'
     html_htm="${*##[0-9][0-9][0-9][0-9]_--_*.index}"
     echo "html_htm: "$html_htm
}

echo 'Verzeichnisname $*: '$verzeichnisname
echo "gewünschtes Ergebnis: html_htm: html"
echo
echo
echo "Funktionsaufruf ohne Quoting des Funktionsparameters"
echo 'eine_funktion $verzeichnisname'
eine_funktion $verzeichnisname
echo
echo
echo "Funktionsaufruf mit Quoting des Funktionsparameters"
echo 'eine_funktion "$verzeichnisname"'
eine_funktion "$verzeichnisname"
Mag ja sein, dass das ein wenig speziell und das if-Konstrukt etwas komplex ist, aber bei einer Skriptlänge von über 1300 Zeilen muss man es etwas kompakter gestalten... :wink: - und letztlich sollte es auch mit Datei- bzw. Verzeichnisnamen funktionieren, die Leerzeichen enthalten - es soll ja Leute geben, die so etwas verwenden :evil:

Vielen Dank jedenfalls!!!

Benutzeravatar
Meillo
Moderator
Beiträge: 9235
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: bash: Substring entfernen

Beitrag von Meillo » 08.12.2015 16:37:44

k2 hat geschrieben: das Problem, oder nennen wir es die Eigenart, liegt definitiv bei der Variablensubtitution mit $* wenn $* Leerzeichen enthält.
Das kann ich bestaetigen, nun, da ich in der Dokumentation nachgelesen habe (letzter Satz!):
Manpage bash(1) hat geschrieben: ${parameter#word}
${parameter##word}
Remove matching prefix pattern. The word is expanded to produce
a pattern just as in pathname expansion. If the pattern matches
the beginning of the value of parameter, then the result of the
expansion is the expanded value of parameter with the shortest
matching pattern (the ``#'' case) or the longest matching pat‐
tern (the ``##'' case) deleted. If parameter is @ or *, the
pattern removal operation is applied to each positional parame‐
ter in turn, and the expansion is the resultant list. If param‐
eter is an array variable subscripted with @ or *, the pattern
removal operation is applied to each member of the array in
turn, and the expansion is the resultant list.
Dagegen meint allerdings die mksh (wieder am Ende):
Manpage mksh(1) hat geschrieben: ${name#pattern}
${name##pattern}
If pattern matches the beginning of the value of parameter name,
the matched text is deleted from the result of substitution. A
single ‘#’ results in the shortest match, and two of them result
in the longest match. Cannot be applied to a vector (${*} or
${@} or ${array[*]} or ${array[@]}).
Das Verhalten in diesem Fall ist also unportabel, wie auch POSIX explizit bestaetigt:
POSIX hat geschrieben: If parameter is '#', '*', or '@', the result of the expansion is unspecified.
Ich halte es fuer das Beste, eine andere Variable zu verwenden. Zum Beispiel $1, denn es ist ja nur ein Argument, wobei das natuerlich auch bedeutet: ...
k2 hat geschrieben: 2. beim Aufruf der Funktion den Funktionsparameter quoten: funktion "$var"
Das nicht zu tun ist sowieso grob fahrlaessig! Wenn auch dein Hack mit $* das Problem elegant zu loesen scheint, so tut er das nicht, denn er scheitert auf unerwartete Weise, wenn der Dateiname mehrere Leerzeichen am Stueck oder Tabs enthaelt.

Merke: In der Shell sollte man Variablenexpansionen immer doublequoten, ausser man weiss was man tut.
Use ed once in a while!

k2
Beiträge: 132
Registriert: 14.08.2002 22:29:28
Lizenz eigener Beiträge: MIT Lizenz

Re: [gelöst] bash: Substring entfernen

Beitrag von k2 » 08.12.2015 17:31:06

If parameter is @ or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list
Bash-Manpage - na das hätte ich ja auch ein wenig genauer lesen können - genau so hat sie sich verhalten wenn ich den Funktionsaufruf nicht quote ... Sorry :facepalm:

Ich quote mal den Funktionsaufruf, dann funktionert es wenigstens, auch wenn die Variable im Pattern-Matching dann immer noch nicht gequotet ist und es so laut Bash-Manpage nicht funktionieren dürfte...

Schade dass das nicht dem Verhalten der Korn-Shell entspricht, ich dachte das Pattern-Matching mit [[ ... ]] hätte die Bash von der Korn übernommen.

Danke nochmals

Benutzeravatar
Meillo
Moderator
Beiträge: 9235
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: [gelöst] bash: Substring entfernen

Beitrag von Meillo » 08.12.2015 18:15:47

k2 hat geschrieben:
If parameter is @ or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list
Bash-Manpage - na das hätte ich ja auch ein wenig genauer lesen können - genau so hat sie sich verhalten wenn ich den Funktionsaufruf nicht quote ... Sorry :facepalm:
Genauso verhaelt es sich auch, wenn du das Argument quotest, nur entspricht dann das Verhalten dem Erwuenschten. ;-)

Ich quote mal den Funktionsaufruf, dann funktionert es wenigstens, auch wenn die Variable im Pattern-Matching dann immer noch nicht gequotet ist und es so laut Bash-Manpage nicht funktionieren dürfte...
Das verstehe ich nicht. Warum sollte es dann nicht funktionieren?

Quote das Funktionsargument und verwende $1 statt $* und alles ist gut.

Schade dass das nicht dem Verhalten der Korn-Shell entspricht, ich dachte das Pattern-Matching mit [[ ... ]] hätte die Bash von der Korn übernommen.
[[ ... ]] macht kein Pattern-Matching, sondern wertet Ausdruecke aus: ``Conditional Command'', d.h. die modernere Alternative zu test(1). Das kommt tatsaechlich von der ksh. Das hat aber gar nichts mit unserem Thema zu tun. Worum sich dein Problem dreht ist nur, was die Bedeutung von ${*##...} ist, und da sind sich die Shells uneinig und das was du dachtest, was die Bash damit macht, war etwas anderes als sie tatsaechlich damit macht.
Use ed once in a while!

k2
Beiträge: 132
Registriert: 14.08.2002 22:29:28
Lizenz eigener Beiträge: MIT Lizenz

Re: [gelöst] bash: Substring entfernen

Beitrag von k2 » 09.12.2015 10:48:33

Meillo hat geschrieben: Quote das Funktionsargument und verwende $1 statt $* und alles ist gut.
Na dann mache ich das doch so!
:THX: Danke Dir

Antworten