ubuntuusers.de

Diverse Quoting-Probleme

Status: Ungelöst | Ubuntu-Version: Xubuntu 24.04 (Noble Numbat)
Antworten |

glaskugel

Anmeldungsdatum:
8. Juli 2010

Beiträge: 4224

Ich möchte verhindern, dass bestimmte Zeichen in den Optionen eines Scripts vorkommen bzw. wenn, dann diese eventuell mit sed ersetzen.

Es geht um

/ \ : |

Eigentlich sind das Dinge mit denen ich noch keine Probleme hatte. Gerade tut der Backslash wieder wie er soll, aber dafür was anderes nicht. Fangen wir mal an:

$ var="Teil1 \ Teil2"
$ echo "$var" | grep -e "\/" -e "\\"
Teil1 \ Teil2

passt also.

$ echo "$var" | grep -e "\/" -e "\\" -e "\|"
grep: Angehängter Backslash (»\«)

Ähnlich:

$ echo "$var" | grep -e "\/" -e "\\" -e "\:"
grep: Angehängter Backslash (»\«)

Auch:

$ echo "$var" | grep -e "\/" -e "\\" -e ":"
grep: Angehängter Backslash (»\«)

TK87

Anmeldungsdatum:
8. Juli 2019

Beiträge: 301

Wohnort: Aachen

Moin,

glaskugel schrieb:

Ich möchte verhindern, dass bestimmte Zeichen in den Optionen eines Scripts vorkommen

was sind "Optionen eines Skripts"?!? Die Argumente oder was?

Ich sehe da keine Frage. Du schreibst, du willst Zeichen mit sed ersetzen, arbeitest aber nur mit grep?!

Gruß Thomas

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 4224

Sorry, wollte mein Posting gerade ändern. Das Problem sind Double-Quotes statt Single Quotes, nur das funktionierte davor auch nur mit Fehlermeldungen. Im Script könnten es noch andere Quoting-Probleme mit Variablen gewesen sein. Backslash-Ersetzen mit sed wollte gar nicht funktionieren, andere Zeichen wie / schon.

$ var="Teil1 \ Teil2"

$ echo "$var" | grep -e '\/' -e '\\' -e '\:' -e '\|'
Teil1 \ Teil2

Das ist mal die Lösung zu meiner ursprünglichen Frage, das war aber nur ein Workaround für mein sed-Problem. Alles nach der Reihe. Ich erstelle ein simples Script zum wav nach mp3 wandeln. für einen Bekannten. Soweit kein Problem und jetzt fallen mir immer mehr Dinge ein, die darum herum große Problem machen können-

Idee ist folgende:

Dem Bash-Script werden 3 Argumente (Optionen) mitgegeben, die dann auch für Dateinamen verwendet werden sollen. So genau weiß ich noch nicht, was ich da alles brauche.

  • 1. Argument: Quell-Pfad

  • 2. Argument: Albumname

  • 3. Argument: Artistname

Der Albumname und Artistname soll auch ein Verzeichnis werden (mehr oder weniger, da dann Kleinschreibung, keine Leerzeichen etc). Die Metadaten haben aber Leerzeichen, Klein- Großschreibung)

Die Dateien (private Audioaufnahmen von Musikern) werden auch weiter gegeben und zwar auch an Windows oder IOS mit eventuell völlig anderem Zeichensatz am PC, zB japanisch.

Damit müssen die Argumente entsprechend bereinigt oder automatisch korrigiert werden. Mittlerweile denke ich, ich verbiete "/ \ : |" in den Argumenten und prüfe das, ursprünglich dachte ich, ich ersetze diese Zeichen mit sed. Also ein / im Albumnamen würde nicht 1 Verzeichnis erstellen, sondern 2. Das | Zeichen reserviere ich als Delimiter.

Letzter Stand ist also, ich prüfe gleich am Anfang, ob die verbotenen Zeichen vorkommen, gebe eine Fehlermldung aus und breche ab.

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1449

Gerade tut der Backslash wieder wie er soll, aber dafür was anderes nicht.

Dein Problem ist etwas komplex und abhängig davon, wie du quotest. Mit einem Quoten über "Gänsefüßchen" (double quote 0x22) wird der gequotete String z.B. hinsichtlich "$"-Substitution untersucht. Dabei wird das Backspace-Escapen vorher umgesetzt. Aus deinem "\\" bleibt dann ein einfacher Backslash übrig, weshalb grep die Fehlermeldung mit dem "Angehängten Backslash" liefert.

Wenn du einen Text mit "single quote" (0x27) definierst, ist eine "$"-Substitution nicht möglich und die Backspace-Escapes bleiben bestehen.

Um näher zu verstehen, was passiert, verwende ich "sed", da dies den "--debug" Qualifier unterstützt und man somit den Zusammenhang besser nachvollziehen kann:

$ echo "$var"
Teil1 \ Teil2
$ echo "$var" | sed --debug -nE "/\/|\\|\:/p"
SED PROGRAM:
  /\/|\\|\\:/ p
INPUT:   'STDIN' line 1
PATTERN: Teil1 \\ Teil2
COMMAND: /\/|\\|\\:/ p
END-OF-CYCLE:
$ echo "$var" | sed --debug -nE '/\/|\\|\:/p'
SED PROGRAM:
  /\/|\\\\|\\:/ p
INPUT:   'STDIN' line 1
PATTERN: Teil1 \\ Teil2
COMMAND: /\/|\\\\|\\:/ p
Teil1 \ Teil2
END-OF-CYCLE:
$ 

Du kannst natürlich auch mit "Gänsefüßchen" quoten, musst dann aber den Double-Backspace verdoppeln, damit nach der Backspace-Escape Aktion der doppelte Backspace verfügbar ist. Beispiel:

$ echo "$var"
Teil1 \ Teil2
$ echo "$var" | grep -E "\/|\\|\:"
$ echo "$var" | grep -E "\/|\\\\|\:"
Teil1 \ Teil2
$ # oder mit dem "-e" Schalter
$ echo "$var" | grep -e "/" -e "\\" -e "\:"
grep: Angehängter Backslash (»\«)
$ echo "$var" | grep -e "/" -e "\\\\" -e "\:"
Teil1 \ Teil2
$ echo "$var" | grep -e '/' -e '\\' -e '\:'
Teil1 \ Teil2
$ 

Ich hoffe, ich konnte diesen Zusammenhang verständlich erläutern.

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 4224

Ich hoffe, ich konnte diesen Zusammenhang verständlich erläutern.

Ja danke, habe ich mittlerweile auch im Web erklärt gefunden, allerdings kompliziert.

Als Frage bleibt wie ich mein Problem "elegant" löse. Man denkt, das ist was einfaches, so ein Script mit lame und eyeD3, aber das herum kann viel komplexer werden.

Mittlerweile denke ich, ich verbiete "/ \ : |" in den Argumenten und prüfe das,

Kann man das besser als mit grep -e machen?

TK87

Anmeldungsdatum:
8. Juli 2019

Beiträge: 301

Wohnort: Aachen

Grep ist da schon die beste Wahl. Einfach dein Skript wie folgt beginnen und gut ist...

1
2
3
4
5
6
#!/bin/bash

if grep -q '[/\|:]' <<<$@;then
  # Hier deine Fehlermeldung einsetzen
  exit 2
fi

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 4224

Grep ist da schon die beste Wahl.

Danke, ist aber trotzdem viel besser / kürzer als ich es überlegt hatte. Ich muss sowieso noch jedes Argument einzeln analysieren. Warum machst du "exit 2"?

Kann man da vielleicht auch noch gleich integrieren, dass es genau 3 Argumente geben muss?

TK87

Anmeldungsdatum:
8. Juli 2019

Beiträge: 301

Wohnort: Aachen

glaskugel schrieb:

Warum machst du "exit 2"?

Naja exit halt um das Skript zu beenden. Statt der 2 kannst du natürlich einen beliebigen Exitcode wählen, oder die auch einfach weglassen. Hilft schon mal bei der Analyse, wenn man klar definierte Fehlercodes hat.

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 4224

Einfach dein Skript wie folgt beginnen und gut ist...

Leider nein, aber klar du bist gedanklich nicht so im Skript wie ich

$1 ist der Dateipfad, der enthält immer / und damit bricht das Skript immer ab.

Ich bin mir auch nicht mehr sicher, ob es Sinn macht am Anfang gleich auf 3 Argumente zu prüfen.

Beim 1. Argument prüfe ich, ob das Argument überhaupt existiert und wenn ja, dann ob der Pfad existiert.

Argument 2 und Argument 3 müssen existieren. Dann kommt je nach fehlendem Argument (2, 3 oder beide) eine entsprechende Fehlermeldung.

Man soll das Skript solange ohne Eingabe von Argumenten aufrufen, bis man die Argumente mit Hinweisen verstanden hat.

1. Argument: Quell-Pfad

2. Argument: Albumname

3. Argument: Artistname

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1449

Kann man das besser als mit grep -e machen?

Verwende das Werkzeug, das du beherrschst. Ob "grep" oder "sed" musst du selbst entscheiden. Bei einigen "regex" Konstrukten ist "grep" etwas leistungsfähiger als "sed". Aber "sed" kann dafür bei der Suche einiges andere mehr (block-search usw) und auch gleich Änderungen durchführen.

Ich bin ob deiner weiteren Postings nun aber etwas unsicher, was du wirklich für eine Frage hast. Wenn du die ID3 Tags bei der mp3 Datei setzen willst und hierbei Probleme beim Escapen siehst, gibt es aus meiner Sicht mehrere Möglichkeiten:

  1. Verwende statt "eyeD3" ein anderes Werkzeug wie "mp3tag" (siehe Paket mp3blaster). Da kannst du deine Tags als CSV in einer Datei ablegen, auf die du beim Aufruf über "%tag%" mappen kannst

  2. Verwende die bash Funktion $(quote "Text oder Variable"). Dies deckt nicht unbedingt alles ab.

  3. Ersetzte die problematischen Zeichen durch die Hex-Äquivalente (\xab) z.B. $(echo -e "Das ist \x22, \x5c und \x27 Text"). Eine Variante für diese Vorgehensweise ist z.B. der Befehl "urlencode" oder "awk"/"sed".

Welche die für dich beste Lösung ist, hängt davon ab, woher du die Sollwerte für die Tags bekommst.

PS: Anbei ein Beispiel mit "eyeD3":

$ eyeD3 -a "artist" -A "Album" -t "Title" -n 1 -G "Genre" -Y 2025 -c "$(echo -e "Das ist \x22, \x5c und \x27 Text")" x.mp3
/home/shiro/Downloads/x.mp3                                      [ 562.36 KB ]
--------------------------------------------------------------------------------
Setting artist: artist
Setting album: Album
Setting title: Title
Setting genre: Genre
Setting release year: 2025
Setting comment: /eng
Time: 01:12	MPEG2, Layer III	[ 64 kb/s @ 24000 Hz - Joint stereo ]
--------------------------------------------------------------------------------
ID3 v2.3:
title: Title
artist: artist
album: Album
release date: 2025
original release date: 2025
recording date: 2022-04-25T17:03
track: 1		genre: Genre (id None)
Comment: [Description: ] [Lang: eng]
Das ist ", \ und ' Text
Writing ID3 version v2.3
--------------------------------------------------------------------------------
$ 
$ # oder
$ read xyz <<<$(cat - <<**EOF**           
Das ist ", \ und ' Text mit | pipe
**EOF**
)
$ eyeD3 -a "artist" -A "Album" -t "Title" -n 1 -G "Genre" -Y 2025 -c "$xyz" x.mp3
/home/shiro/Downloads/x.mp3                                      [ 562.36 KB ]
--------------------------------------------------------------------------------
Setting artist: artist
Setting album: Album
Setting title: Title
Setting genre: Genre
Setting release year: 2025
Setting comment: /eng
Time: 01:12	MPEG2, Layer III	[ 64 kb/s @ 24000 Hz - Joint stereo ]
--------------------------------------------------------------------------------
ID3 v2.3:
title: Title
artist: artist
album: Album
release date: 2025
original release date: 2025
recording date: 2022-04-25T17:03
track: 1		genre: Genre (id None)
Comment: [Description: ] [Lang: eng]
Das ist ",  und ' Text mit | pipe
Writing ID3 version v2.3
--------------------------------------------------------------------------------
$ 

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 4224

Verwende das Werkzeug, das du beherrschst.

Immer nur basics bei grep und sed, aber damit komme ich im Normalfall schon aus, wenn auch vielleicht umständlich.

Verwende statt "eyeD3

Nein, das passt schon (fast) alles bis auf einen Fehler mit encoding bei eyeD3, aber das kann man auch weglassen und ich glaube das ist ein Bug. Es geht nur darum Fehler danach beim Umbenennen abzufangen.

Basis ist ja wav ohne Metadaten und es geht ja darum, was automatisch reinzuschreiben.

Die Track-Nr. ergibt sich durch die Sortierung nach der Zeit (=Dateiname der Aufnahme)

Titel ist schwierig, das ist der Datiname der Aufnahme ohne wav

Jahr ergibt sich auch nach dem Dateinamen, das Jahr schreibt der Rekorder am Anfang.

Artist und Album sind die Scriptargumente. Da ist ein / kein Problem bei den mp3-Metadaten.

Kommentar ist der ursprüngliche Dateiname.

Viel mehr kann man aus der wav-Datei nicht rauskltzeln.

lame -V1 --vbr-new "$input" -o "$outputfile"
eyeD3 -n $i -t "$recordingtime" -Y $yeartag -A "$albumname" -a "$artistname" --comment=::"$kommentar" "$outputfile"

Nimm einfach als Albumname "Probe / November" an. Wenn ich dann aus diesem Argument 1 Verzeichnis erstellen will, dann entsteht Probe und darunter November. Jetzt könnte ich sagen, ich ersetze automatisch / durch - mit sed und alles ist gut oder ich zwinge den User selber ein anderes Zeichen statt / zu verwenden und letzteres ist die augenblickliche Strategie. Solche Dinge müssen am Anfang geprüft werden und zur Not schaffe ich das auch alleine, aber meistens viel umständlicher als ihr.

Sonst läuft das Script durch wie es soll mit normalen Argumenten, die Umbenennung und Aufteilen in Verzeichnisse ist noch holprig kodiert, funktioniert aber wie gewünscht. Die Probleme entstehen, wenn der User Argumente angibt, die Betriebssystem bedingt zu Fehlern führen.

kB Team-Icon

Supporter, Wikiteam
Avatar von kB

Anmeldungsdatum:
4. Oktober 2007

Beiträge: 10197

Wohnort: Münster

glaskugel schrieb:

[…] verbiete "/ \ : |" in den Argumenten und prüfe das,

Kann man das besser als mit grep -e machen?

grep ist ein gutes Werkzeug, man kann es aber auch alleine mit Sprachmitteln der bash lösen. Hierzeu ein Testskript:

#! /bin/bash -e

echo "Ich (${0##*/}) wurde mit $# Argumenten gestartet:"	>&2
: Prüfung auf mindestens 3 Argumente ${3?Benötige mindestens 3 Argumente!}
printf '%s\n' "${@}"						>&2

echo "Nach Bereinigung von '/\|:' :"				>&2
for x
do	##echo $x						>&2
	echo ${x//[\/\\\\:\|]/}
done

test $# = 3 || { echo Erwarte genau 3 Argumente ; exit 3 ;}

Aufruf z.B. mit:

./testskript a/ b\\ c\|: d '' 6 '7@\|:' ; echo $? 

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 4224

grep ist ein gutes Werkzeug, man kann es aber auch alleine mit Sprachmitteln der bash lösen

Danke, aber ich verstehe da einiges nicht, speziell in Hinblick auf das konkrete Problem.

#! /bin/bash -e

Was bewirkt das "-e"? Man bash hat mich nicht wirklich schlauer gemacht. Läuft das auf interaktiv hinaus?

Wie schon angedeutet, ich glaube mittlerweile es bringt nichts abzufragen, ob es 3 Argumente gibt. Wenn es die nicht gibt, muss erst geprüft werden, was falsch ist. Aber gut zu wissen und ich erinnere mich jetzt wieder, ich hatte mal schon so ein Konstrukt in einem anderen Skript verwendet, vergisst man, wenn man es Jahre nicht benötigt.

Ich erkläre dem User/n persönlich, es sind 3 Argumente anzugeben. Die Quelldateien kommen im Normalfall von einem "Handy-Sync-Script per lftp", der Pfad enthält US-ASCII und keine Leerzeichen, ist aber je nach PC anders. Das könnte man automatisieren, dann wird es aber unflexibel, wenn man je nach Situation einen anderen Pfad hat. Das führt jetzt zu weit weg. Nach dem Sync muss am Handy gelöscht werden, damit Dateien nicht doppelt auf den PC gelangen, etc. Da ist eine ganze Menge organisatorisch notwendig,

Problem könnte noch werden, wenn eine andere Aufnahme-App am Handy verwendet wird, die eine andere Dateibenennung als default hat. Das lasse ich auf mich zukommen, zu 99,99% wird Fieldrecorder am Android-Handy verwendet. Es könnte auch noch Neutron oder ASR verwendet werden, das müsste man dann nach dem Default-Dateimuster erkennen. Diese Möglichkeit ignoriere ich erst mal.

Wenn man sich als Albumname "Probe - November" wünscht, dann muss man Anführungszeichen verwenden, Fertig mit Erklärungen an den User. Die wichtigsten Fehler werden versucht abzufangen, alles ist mir zu aufwendig. Es wäre sehr unwahrscheinlich "Probe \ November" zu verwenden und dann die Datei an einen Windows-User weiterzugeben. Keine Ahnung was da Windows macht, wenn da so eine Datei angeboten wird. Um Problemen vorzubeugen, wird einfach das Zeichen verboten.

Ich bin bis jetzt für das 1. Argument so vorgegangen, das kann durchaus besser formuliert werden, aber funktioniert.

if [ -z "$1" ]; then
	echo
	echo "Quellverzeichnis ist nicht angegeben"
	echo "Quellverzeichnis angeben und Skript erneut aufrufen"
	echo
	exit
fi

if [ -d "$1" ]; then
	cd "$1"
else
	echo
	echo "Quellverzeichnis wurde nicht gefunden"
	echo "Bestehendes Quellverzeichnis angeben und Skript erneut aufrufen"
	echo
	exit
fi

Fehlt das 1. Argument, interessiert mich noch nicht, dass das 2. und 3. Argument auch fehlt. Ich habe den Aufruf erklärt und wenn der vergessen wurde, ruft man solange auf bis 3 Argumente angegeben wurden.

Das was ich bis jetzt für das 2. Argument hatte, passt nicht mehr, nachdem ich die genannten Zeichen nun gar nicht mehr erlaube.

Ich breche also immer mit einem Hinweis ab, wenn eine grundsätzliche Logik nicht passt. Kein 2. Argument, Abbruch mit Hinweisen.

echo "Nach Bereinigung von '/\|:' :"				>&2
for x
do	##echo $x						>&2
	echo ${x//[\/\\\\:\|]/}
done

Das verstehe ich nicht ganz. Ich hatte in dieser Richtung gedacht, aber das passt eben für den Pfad nicht:

if grep -q '[/\|:]' <<<$@;then
	echo
	echo "Albumname und / oder Artistname enthalten nicht erlaubte Zeichen"
	echo "Verboten sind / \ | :"
	echo "Bitte korrigieren und Skript neu starten"
	echo
	exit 2
fi

Vielleicht könnte man es so ähnlich mit anderer Info machen.

if grep -q '[/\|:]' <<<$2;then

Ich finde die Schleife (für mich) nach Jahren schlecht lesbar / verständlich, was da gemacht wurde, grep ist für mich viel verständlicher

test $# = 3 || { echo Erwarte genau 3 Argumente ; exit 3 ;}

Ich glaube, das kann man sich sparen. Wenn es passt, läuft das Script, sonst wird es mit Hinweis wie an den Beispielen davor abgebrochen. Aber ok, ich hatte mal danach gefragt.

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4773

Wohnort: Berlin

@glaskugel Egal was falsch ist es sollte immer erklärt werden wie der Aufruf aussehen muss. So ziemlich jedes Programm macht das so sofern es nicht eine extra -h/--help-Option dafür gibt. Die die das nicht machen sind an der Stelle einfach nur sehr benutzerunfreundlich. Freundlich formuliert. Niemand will ein Programm mehrfach aufrufen, und sich von Fehler zu Fehler hangeln, nur weil das Programm den grundsätzlichen Aufruf als Staatsgeheimnis behandelt.

Wenn etwas mit den Argumenten nicht stimmt, sollte immer etwas wie

echo "Aufruf: $(basename "$0") QUELLPFAD ALBUMNAME KUENSTLERNAME"

ausgegeben werden. Falls gar kein Argument übergeben wurde, am besten noch weitere Hinweise, zum Beispiel welche Zeichen verboten sind. Ansonsten noch eine spezifischere Meldung was konkret falsch war, zum Beispiel verbotene Zeichen oder das der Quellpfad nicht existiert.

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 4224

Ok, kann ich ja noch machen. Das ist ein Script für ein paar Leute. Eigentlich wollte ich nur ein GUI empfehlen um von wav zu mp3 zu wandeln, aber letztlich macht das Organisatorische doch viel Arbeit und Klickerei.

Ich habe die erlaubten Zeichen geändert und es funktioniert nicht mit "-"

if grep -q '[/\:-|]' <<<$2; then
  • /\: sind wegen OS-Dingen gesperrt

  • - ist im Dateinamen Trenner zwischen Artist und Album

  • | reserviere ich als eventuellen Delimiter, wer weiß was noch kommt.

In der Praxis wird das kaum ein Problem bei Musikproben sein. Die Beispiele, wo es haken könnte, sind eher theoretisch.

Warum klappt das mit "-" nicht, mit den anderen aber schon? Habe noch nicht alles durchprobiert.

Antworten |