MartinV hat geschrieben: 17.10.2019 23:12:04
Die Kombination von -c und -s ist speziell. Aus der Manpage ist nicht offenkundig klar, das -s die Komplementärmenge von MENGE1 verwenden wird, wenn -c gesetzt ist.
Ja, die Manpage koennte hier etwas genauer sein. Fuer mich war es aber einleuchtend, weil es nur so Sinn macht.
Einen Effekt verstehe ich aber nicht. In MENGE1 hast Du auch - als zulässiges Zeichen definiert. Option -s entfernt aber nur doppelte Zeichen, die nicht in MENGE1 enthalten sind.
Das würde bedeuten, daß ein Vorkommen von --- im ursprünglichen String erhalten bleiben sollte. Es wird aber dennoch auf ein - reduziert:
Code: Alles auswählen
$ printf "aaa---bbb" | tr -cs "a-zA-Z0-9_-" "-"
aaa-bbb
Sehr gut aufgepasst! Mir ist das auch (beim Durchdenken) aufgefallen. Daraufhin habe ich es getestet: Egal ob das Minus in der ersten Menge ist, es wird immer gesqueezet. Das sieht mir nach einem Bug aus. Naeher recherchiert (neueste Version von tr, Bugtracker) habe ich aber noch nicht. [siehe unten]
Bei Deinem Vorschlag überblicke ich nicht, was da eigentlich passiert:
Ich ersetze eine komplette Zeile, die mit optionalen Minusen anfaengt und mit optionalen Minusen aufhoert, durch das was zwischen diesen Minusen steht. Eigentlich sollten der erste und letzte Stern Fragezeichen sein, weil ich hier nur auf null-oder-eins pruefen will, aber da `tr -s' maximal eines erzeugt, macht das keinen Unterschied.
Wenn ich in der Klammer
..* (als Aequivalent zu
.\+, aber portabel) schreiben wuerde, dann wuerde das abschliessende Minus immer in der Klammer landen, weil POSIX Regexp gierig sind. Die Klammer wuerde also die komplette Zeile auffressen und das optionale Minus am Ende wuerde stets leer ausgehen. (Ungreedy-Modifier, wie bei PCREs, gibt es bei POSIX nicht.) Also sage ich, dass in der Klammer beliebig viele optionale Zeichen stehen koennen, gefolgt von einem noetigen Zeichen, das kein Minus ist. Falls am Ende ein Minus sein sollte, landet es dadurch hinter der Klammer. Falls die Zeile nur aus Minusen bestehen sollte -- dank `tr -s' ist es maximal eines -- wird nichts ersetzt, weil die Regexp nicht passt.
Ist das klar geworden, oder muss ich etwas davon noch genauer erklaeren?
Etwas anderes ist mir aufgefallen: echo sendet auch einen Zeilenumbruch, der von tr in - umgewandelt wird:
Ja. Ich bin davon ausgegangen, dass die Funktion in Scripten auf Strings angewendet werden soll, und dass die keine Newlines am Ende haben. Da du die Minuse eh wegfiltern willst, eruebrigt sich dieses Thema aber.
"echo -n" würde das vermeiden, aber ich nutze ungern echo mit Optionen, da sich echo auf verschiedenen Systemen sehr unterschiedlich verhält.
Mit printf kann ich den Zeilenumbruch (und damit ein - ) vermeiden:
Eine Frage dazu: Könnte printf versehentlich ein Argument bekommen, das ungewolltes Verhalten hervorruft? Also irgendetwas als Option interpretiert?
printf(1) ist hier auch die bessere Wahl als `echo -n', aber, wie du schon selber merkst, ist
nicht vorhersagbar. Darum verwende:
... und das Verhalten ist eindeutig definiert.
---------------------
Meine jetzige Version der Funktion für die Threadfrage:
Code: Alles auswählen
unspecialstring() { # replace special chars of $1 with -
# Replace all characters except those described in "a-zA-Z0-9_-" with a '-'.
# Replace newlines, too.
# Avoids double '--'
# Remove leading and trailing '-'
# Return empty string if only special chars are given.
LC_ALL=C printf "${1:-}" | tr -cs "a-zA-Z0-9_-" "-" | sed -e 's/-$// ; s/^-//'
}
Wie gesagt, du brauchst noch ein `%s' beim printf(1), oder du verwendest einfach `echo', weil das Minus durch das Newline am Ende ja wieder entfernt wird.
Nachtrag: Nun habe ich mir `-s' doch etwas genauer angeschaut. Hier aus der Manpage der Heirloom-Tools:
Code: Alles auswählen
-s
squeezes all strings of repeated output characters that
are in string2 to single characters.
Das beschreibt das von uns betrachtete Verhalten korrekt, da es sich die Menge 2 (``string2'') und nicht auf die Menge 1 bezieht. Nach der Beschreibung ist es folglich egal, ob das Minus in Menge 1 vorkommt oder nicht.
Und hier aus der POSIX-Manpage:
Code: Alles auswählen
-s
Replace instances of repeated characters with a sin-
gle character, as described in the EXTENDED DESCRIP-
TION section.
[...]
When the -s option is specified, after any deletions or
translations have taken place, repeated sequences of the
same character shall be replaced by one occurrence of the
same character, if the character is found in the array spec-
ified by the last operand.
Auch das bezieht sich auf die Menge 2 (``last operand'').
Fazit: Die GNU-Manpage (tr-8.14) sieht fuer mich falsch aus. Sie entspricht weder POSIX noch ihrer eigenen Implementierung.