ubuntuusers.de

Leerzeichen in Shell- und Programmparametern

Status: Gelöst | Ubuntu-Version: Ubuntu 12.04 (Precise Pangolin)
Antworten |

zebulon-ufh

Avatar von zebulon-ufh

Anmeldungsdatum:
12. Dezember 2016

Beiträge: 50

Hallo!

Ich stehe hier vor dem Problem, eine pdf-Datei, die aus einer Windows-Applikation unter Wine kommt per evince zu öffnen. Die Applikation erzeugt Files nach dem Muster "z:\tmp\file.pdf" und übergibt den Namen durchaus an die Linux-Applikation. Hier evince. Da evince nun aber mal genau mit diesem Parameter nichts anzufangen weiß, geht das zunächst an folgendes Shellscript:

#!/bin/bash
# Backslashes durch Slashes ersetzen:
File=${@//\\//}
# z: entfernen und den Pfad nach /tmp absolut machen:
File="/"${File:3}
# Zur Kontrolle, welcher Name übergeben wurde, loggen:
echo $File>~/pdf.log
/usr/bin/evince $File

Danach heißt dann der dem evince übergebene Paramater "/tmp/file.pdf", wo die Datei auch tatsächlich liegt. Das geht auch wunderhypsch, solange "File" keine Leerzeichen enthält. Die erste Hürde war durch Verwendung von "$@" statt "$1" genommen, um Parameter mit Leerzeichen als einen einzigen zu interpretieren. So klappt zwar dieses Script auch bei Leerzeichen, während

 $File=`winepath -u $@` 

dahingehend versagt, mehrere Unix-Pfade zu erzeugen, sobald Leerzeichen im Parameter sind. Das Script nutzt aber auch nichts, da evince wohl Anführungszeichen um Dateinamen haben möchte, die Leerzeichen enthalten. Wie und wo bekomme ich die im Script drumrum? Was schonmal nicht geht, ist

File=\""/"${File:3}\"

oder

/usr/bin/evince "$File"

Damit findet Evince genau einmal keinen File.

/usr/bin/evince \"$File\"

lässt evince je nach Anzahl der Leerzeichen entsprechend viele Files nicht finden. Und nun bin ich mit meinem Latein am Ende. Hat jemand nen heißen Tipp für mich?

CU!

Ulrich

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13176

Nicht evince will Anführungszeichen um die Namen haben sondern die Shell. Du müsstest Dich mal mit Quoting beschäftigen. Das Problem ist aber folgendes: die Namen "a<Leerzeichen>b" und "a<Leerzeichen><Leerzeichen>b" können nicht mehr unterschieden werden, wenn sie erst mal ohne richtiges Quoting übergeben worden sind:

1
2
3
4
5
6
7
8
$ args.sh a b
2 args
arg[1]: <a>
arg[2]: <b>
$ args.sh a  b
2 args
arg[1]: <a>
arg[2]: <b>

Ich weiß nicht genau, was Windows-Programme da machen, aber von meiner Erfahrung her und dem, was Du beschreibst, würde ich sagen, dass ein Name in mehreren Shell-Argumenten übergeben wird, so dass man genau in der Situation landet, die ich oben beschrieben habe. Das könnte dann so aussehen:

1
2
3
4
5
6
7
8
$ args.sh \"a b\"
2 args
arg[1]: <"a>
arg[2]: <b">
$ args.sh \"a  b\"
2 args
arg[1]: <"a>
arg[2]: <b">

Wenn Du die jetzt zusammenfügst, bzw "$*" benutzt, kannst Du die beiden Fälle nicht mehr unterscheiden:

1
2
3
4
$ sh -c 'echo @"$*"@' -- \"a b\"
@"a b"@
$ sh -c 'echo @"$*"@' -- \"a  b\"
@"a b"@

Ich sehe im Moment nicht, wie man das retten kann. Du könntest das Skript von meiner Benutzerseite nehmen, um das mal auszuprobieren.

Übrigens:

1
$File=`winepath -u $@`

sollte so aussehen

1
$File=$(winepath -u "$*")

wenn es nur ein Dateinamen sein soll. Aber, wie gesagt, das Problem von oben bleibt, weil die Spaces in einen zusammen fallen, es sei denn, der Name wird nur in einem Argument übergeben.

zebulon-ufh

(Themenstarter)
Avatar von zebulon-ufh

Anmeldungsdatum:
12. Dezember 2016

Beiträge: 50

rklm schrieb:

Nicht evince will Anführungszeichen um die Namen haben sondern die Shell.

Sorry. Aber bei Deiner Antwort verstehe ich nur Bahnhof und Kofferklauen. Vielleicht habe ich das Problem aber auch nur unverständlich formuliert. Direkt auf der Shell habe ich keinerlei Probleme, dem evince Parameter zu übergeben, die Leerzeichen enthalten. Da tippe ich einfach zum Beispiel

 evince "/tmp/Wein  Vinos GmbH - Rechnung 004101481.pdf" 

Und evince öffnet mir genau diesen File. Findet ihn. Möchte nicht etwa nur "/tmp/Wein" oder gar fünf Files "tmp/Wein", "Vinos", "GmbH", "-", "Rechnung" und "004101481.pdf" öffnen.

Nun möchte ich das Gleiche per Shellscript auslösen. In "$File" steht nun

/tmp/Wein  Vinos GmbH - Rechnung 004101481.pdf

Evince - oder von mir aus auch die Shell - möchte drumherum Anführungszeichen haben, wie ich es zu Fuß habe verifizieren können. Wie bekomme ich die per genanntem Script drumherum, auf daß der Aufruf aus dem genannten Bash-Script genauso klappt wie zu Fuß eingetippt? Genau das bekomme ich aber nicht gebacken.

TIA, Ulrich

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13176

zebulon-ufh schrieb:

rklm schrieb:

Nicht evince will Anführungszeichen um die Namen haben sondern die Shell.

Sorry. Aber bei Deiner Antwort verstehe ich nur Bahnhof und Kofferklauen. Vielleicht habe ich das Problem aber auch nur unverständlich formuliert.

Nein, ich denke, ich habe schon verstanden, was Dein Problem ist.

Direkt auf der Shell habe ich keinerlei Probleme, dem evince Parameter zu übergeben, die Leerzeichen enthalten. Da tippe ich einfach zum Beispiel

Logisch, denn da bewegst Du Dich komplett in Linux-Land. Dort wird einem Prozess eine Liste von einzelnen Argumenten übergeben, die alles mögliche inklusive Leerzeichen enthalten können. Das stellt man üblicherweise durch das richtige Quoten sicher:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ args.sh a b
2 args
arg[1]: <a>
arg[2]: <b>
$ args.sh 'a b'
1 args
arg[1]: <a b>
$ args.sh "a b"
1 args
arg[1]: <a b>
$ args.sh "a  b"
1 args
arg[1]: <a  b>

Die Anführungsstriche werden von der Shell ausgewertet und der aufgerufene Prozess sieht die nie. Im Klartext: der Aufrufer ist dafür zuständig, diese Argumentliste passend zusammen zu bauen (siehe Shell/Bash-Skripting-Guide für Anfänger (Abschnitt „Quoting“) und hier, was hier verlinkt ist).

In Windows-Land ist das anders: da übergibt der Aufrufer nur eine lange Zeichenkette, wie Du an diesem Beispiel sehen kannst. Der aufgerufene Prozess sieht also nur ein Argument und muss selbst die einzelnen Argumente parsen. Dafür schaut er auf vorhandene Quotes. Ich bin mir nicht 100% sicher, was passiert, wenn ein Windows-Programm (unter WINE) einen Linux-Prozess aufruft. Ich kann mir vorstellen, dass die gesamte Kommandozeile in einem Argument landet. Wäre das der Fall, dann müsste das Linux-Programm auch erst mal hingehen und die Kommandozeilenargumente parsen - im Prinzip das, was Dein zwischengeschaltetes Skript versucht. Aber: das ist unterkomplex, weil es nicht eine Zeichenkette in verschiedene zerlegt sondern (mit Quotes) einfach als eine behandelt oder (ohne Quotes) nach den Shell-Regeln, die an dieser Stelle keine Rücksicht auf Anführungsstriche legen (was ich versucht habe zu zeigen), in viele zerlegt - und zwar mehr als Dir lieb sind, wenn Leerzeichen in einem Dateinamen enthalten sind.

Wie bekomme ich die per genanntem Script drumherum, auf daß der Aufruf aus dem genannten Bash-Script genauso klappt wie zu Fuß eingetippt? Genau das bekomme ich aber nicht gebacken.

Ein Weg wäre eval:

1
2
3
4
5
6
7
8
$ args='abc "def ghi" jkl'
$ echo "$args"
abc "def ghi" jkl
$ eval "args.sh $args"
3 args
arg[1]: <abc>
arg[2]: <def ghi>
arg[3]: <jkl>

Also, in Deinem Fall

1
eval "evince $*"

Probier es mal aus, ob es klappt.

zebulon-ufh

(Themenstarter)
Avatar von zebulon-ufh

Anmeldungsdatum:
12. Dezember 2016

Beiträge: 50

rklm schrieb:

zebulon-ufh schrieb:

Direkt auf der Shell habe ich keinerlei Probleme, dem evince Parameter zu übergeben, die Leerzeichen enthalten. Da tippe ich einfach zum Beispiel

Logisch, denn da bewegst Du Dich komplett in Linux-Land.

Und wenn ich den Filenamen aus einem Bash-Script übergeben lassen möchte, nicht mehr? Vergiss einfach mal, woher er kommt. Das Bash-Script belegt $File mit foo bar bang bum.pdf. Tippe ich auf der Shell

 evince foo bar bang bum.pdf 

geht das schief. Tippe ich auf der Shell dagegen

 evince "foo bar bang bum.pdf" 

funktioniert das. Wie überrede ich ein Shellscript dazu, dem evince auch "foo bar bang bum.pdf" (also mit Gänsefüßen und so, daß er das nicht als Parameterliste, sondern Filenamen versteht, wie es direkt auf der Shell geht) zu übergeben?

Also, in Deinem Fall

1
eval "evince $*"

Probier es mal aus, ob es klappt.

Also statt

 evince $File 

???

zebulon-ufh

(Themenstarter)
Avatar von zebulon-ufh

Anmeldungsdatum:
12. Dezember 2016

Beiträge: 50

rklm schrieb:

Ich kann mir vorstellen, dass die gesamte Kommandozeile in einem Argument landet. Wäre das der Fall, dann müsste das Linux-Programm auch erst mal hingehen und die Kommandozeilenargumente parsen - im Prinzip das, was Dein zwischengeschaltetes Skript versucht. Aber: das ist unterkomplex, weil es nicht eine Zeichenkette in verschiedene zerlegt sondern (mit Quotes) einfach als eine behandelt oder (ohne Quotes) nach den Shell-Regeln, die an dieser Stelle keine Rücksicht auf Anführungsstriche legen (was ich versucht habe zu zeigen), in >viele zerlegt

Dem war nicht so. Das wine-Programm (übrigens der Forte Agent) übergibt lediglich den Windowsnamen des Files (z.B. z:\tmp\foo bar bang.pdf) und legt den File auch genau dort ab. Eigentlich müsste es reichen (und reicht auch. s.u.) aus dem Argument den Unixnamen /tmp/foo bar bang.pdf zu machen und per

evince "/tmp/foo bar bang.pdf"

dem Dokumentenbetrachter zu übergeben.

Irgendwo hatte ich da aber Sand im Parameterübergabegetriebe. Der übergebene Parameter hieß "foo[ein Leerzeichen]bar[ein Leereichen] bang.pdf", gespeichert wurde aber als "foo[zwei Leerzeichen]bar[ein Leerzeichen]bang.pdf". Da konnte ich lange suchen …

Sobald mir nicht irgendein Leumel ein zusätzliches Leerzeichen in den Filenamen "zaubert", geht es problemlos,

evince "$File"

aufzurufen.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13176

Schreib doch mal oben (also vor jeglicher anderer Verarbeitung) in Dein Skript diese Zeile und poste das Ergebnis, also den Inhalt von /tmp/args.txt:

1
for a; do echo "<$a>"; done >| /tmp/args.txt

zebulon-ufh

(Themenstarter)
Avatar von zebulon-ufh

Anmeldungsdatum:
12. Dezember 2016

Beiträge: 50

rklm schrieb:

Schreib doch mal oben (also vor jeglicher anderer Verarbeitung) in Dein Skript diese Zeile und poste das Ergebnis, also den Inhalt von /tmp/args.txt:

1
for a; do echo "<$a>"; done >| /tmp/args.txt
<z:\tmp\Wein>
<Vinos>
<GmbH>
<->
<Rechnung>
<004101481.pdf>

Aber in dem Hasen lag der Pfeffer nicht. Die Wine-Applikation hatte zwischen z:\tmp\Wein und Vinos zwei Leerzeichen eingefügt:

ulrich@ufh:/tmp$ ls We*
Wein Vinos GmbH - Rechnung 004101481.pdf
Wein  Vinos GmbH - Rechnung 004101481.pdf

Die obige Zeile hatte ich letzendlich als Parameter, der File hieß so wie die untere. Darüber kam ich ins Schleudern. Nun geht es so wie erwartet; der Spuk ist vorbei. Hauptsache, ich nutze $@ statt $1, um den gesamten Dateinamen in einen Parameter zu bekommen und rufe evince mit

evince "$File"

auf.

Das Problem waren also nicht die Leerzeichen im Dateinamen als solche, sondern die unerklärliche Diskrepanz zwischen Dateinamen (zwei Leerzeichen hinter "Wein") und Parameter (Ein Leerzeichen hinter "Wein").

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13176

zebulon-ufh schrieb:

rklm schrieb:

Schreib doch mal oben (also vor jeglicher anderer Verarbeitung) in Dein Skript diese Zeile und poste das Ergebnis, also den Inhalt von /tmp/args.txt:

1
for a; do echo "<$a>"; done >| /tmp/args.txt
<z:\tmp\Wein>
<Vinos>
<GmbH>
<->
<Rechnung>
<004101481.pdf>

Sehr aufschlussreich: das Windows-Programm übergibt den Namen in mehreren Argumenten. Damit hast Du verloren und es gibt keine sichere Lösung, wie Dein Beispiel zeigt:

Aber in dem Hasen lag der Pfeffer nicht. Die Wine-Applikation hatte zwischen z:\tmp\Wein und Vinos zwei Leerzeichen eingefügt:

ulrich@ufh:/tmp$ ls We*
Wein Vinos GmbH - Rechnung 004101481.pdf
Wein  Vinos GmbH - Rechnung 004101481.pdf

Wenn der Name auf diese Weise auf mehrere Argumente verteilt worden ist, hast Du in dem Skript keine Chance ihn immer richtig zusammen zu setzen, weil Du nicht weißt und nicht herausfinden kannst, wie viele Spaces (oder anderer Whitespace wie Zeilenumbrüche) tatsächlich zwischen den Teilen war.

Die obige Zeile hatte ich letzendlich als Parameter, der File hieß so wie die untere. Darüber kam ich ins Schleudern. Nun geht es so wie erwartet; der Spuk ist vorbei. Hauptsache, ich nutze $@ statt $1, um den gesamten Dateinamen in einen Parameter zu bekommen und rufe evince mit

evince "$File"

auf.

Wenn Du das in eins haben willst, dann darfst Du aber nicht $@ sondern musst $* nutzen, denn wenn Du $@ in doppelten Anführungsstrichen verwendet, macht die Shell daraus wieder einzelne Parameter. Wenn Du $* nutzt, setzt sie zwischen jeden einzelnen Parameter exakt ein Leerzeichen.

Das Problem waren also nicht die Leerzeichen im Dateinamen als solche, sondern die unerklärliche Diskrepanz zwischen Dateinamen (zwei Leerzeichen hinter "Wein") und Parameter (Ein Leerzeichen hinter "Wein").

Wie die zustande gekommen ist, habe ich ja oben versucht zu erklären.

zebulon-ufh

(Themenstarter)
Avatar von zebulon-ufh

Anmeldungsdatum:
12. Dezember 2016

Beiträge: 50

rklm schrieb:

Sehr aufschlussreich: das Windows-Programm übergibt den Namen in mehreren Argumenten.

Nein. Es sind nur Leerzeichen im Namen drin. Immer wieder ein Quell der Freude ☹. Leerzeichen, die wohl die Linux-Shell als Trenner für mehrere Argumente interpretiert. Um den Dateinamen als ein (implizit einziges) Argument zu interpretieren, reicht aber ein $@ im Script.

Das Problem waren also nicht die Leerzeichen im Dateinamen als solche, sondern die unerklärliche Diskrepanz zwischen Dateinamen (zwei Leerzeichen hinter "Wein") und Parameter (Ein Leerzeichen hinter "Wein").

Wie die zustande gekommen ist, habe ich ja oben versucht zu erklären.

Das ist dadurch zustande gekommen, daß die Wine-Appikation unverständlicherweise ein zusätzliches Leerzeichen in den Dateinamen reinschummelte, das sie aber nicht im Übergabeparameter berücksichtigte. Warum sie das tat und warum auf einmal nicht mehr, weiß wohl niemand. Auf jeden Fall klappt es jetzt. Der Dateiname wird (auch wenn er Leerzeichen enthält!) dank $@ komplett übernommen. Dank evince "$File" (statt evince $File) bekommt auch evince einen einzigen Parameter übergeben. Funzt!

Nun aber bitte langsam EOT. ☺

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13176

zebulon-ufh schrieb:

rklm schrieb:

Sehr aufschlussreich: das Windows-Programm übergibt den Namen in mehreren Argumenten.

Nein. Es sind nur Leerzeichen im Namen drin. Immer wieder ein Quell der Freude ☹. Leerzeichen, die wohl die Linux-Shell als Trenner für mehrere Argumente interpretiert.

Die Ausgabe, die Du gepostet hast, beweist, dass die Teile des Namens in verschiedenen Argumenten übergeben werden. Da gibt es nichts zu deuteln.

Nun aber bitte langsam EOT. ☺

Wie Du willst. Ich denke immer noch, Du hast es entweder nicht richtig verstanden oder aber es fehlen Informationen.

zebulon-ufh

(Themenstarter)
Avatar von zebulon-ufh

Anmeldungsdatum:
12. Dezember 2016

Beiträge: 50

rklm schrieb:

zebulon-ufh schrieb:

rklm schrieb:

Sehr aufschlussreich: das Windows-Programm übergibt den Namen in mehreren Argumenten.

Nein. Es sind nur Leerzeichen im Namen drin. Immer wieder ein Quell der Freude ☹.

Die Ausgabe, die Du gepostet hast, beweist, dass die Teile des Namens in verschiedenen Argumenten übergeben werden.

Nein. Sie beweist lediglich, daß Deine Form der Abfrage (wie auch $1, $2, $3 …) einen Parameter, welcher n Leerzeichen enthält, als n+1 Argumente interpretiert. Genau deswegen habe ich $@ genommen.

Nun aber bitte langsam EOT. ☺

Wie Du willst. Ich denke immer noch, Du hast es entweder nicht richtig verstanden

Doch. Denn es klappt grundsätzlich so, wie es soll: Parameter-"Annahme" mit $@. Parameterübergabe an evince in "" eingeschlossen. Der Hase lag im "Parameter stimmte nicht mit dem Dateinamen überein. Letzterer enthielt an erster Stelle ein weiteres Leerzeichen im Dateinamen"-Pfeffer. Ist hier vielleicht bitteschön jemand, der mehr Ahnung von der bash als wir beide zusammen hat und der "richten" ☺ mag?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13176

zebulon-ufh schrieb:

rklm schrieb:

Die Ausgabe, die Du gepostet hast, beweist, dass die Teile des Namens in verschiedenen Argumenten übergeben werden.

Nein. Sie beweist lediglich, daß Deine Form der Abfrage (wie auch $1, $2, $3 …) einen Parameter, welcher n Leerzeichen enthält, als n+1 Argumente interpretiert. Genau deswegen habe ich $@ genommen.

Es ist nicht "ein Parameter" sondern es sind mehrere - auch, wenn es inhaltlich ein Wert sein soll. Dein Shellskript erhält aber bereits mehrere Parameter. Du solltest Dich wirklich intensiver mit der Shell, Parameterübergabe und Quoting beschäftigen.

Ist hier vielleicht bitteschön jemand, der mehr Ahnung von der bash als wir beide zusammen hat und der "richten" ☺ mag?

Vielleicht hat jemand anderes mehr Erfolg. Ich habe es mehrfach versucht Dir zu erklären, aber das hat wohl nicht funktioniert.

zebulon-ufh

(Themenstarter)
Avatar von zebulon-ufh

Anmeldungsdatum:
12. Dezember 2016

Beiträge: 50

rklm schrieb:

Es ist nicht "ein Parameter" sondern es sind mehrere

WEGEN DER LEERZEICHEN IM DATEINAMEN !!!

- auch, wenn es inhaltlich ein Wert sein soll.

ES IST EIN EINZIGER DATEINAME !!!

Dein Shellskript erhält aber bereits mehrere Parameter.

WEIL DIE SHELL DIE LEERZEICHEN IM DATEINAMEN ALS PARAMETERTRENNER INTERPRETIERT !!!

Dagegen hilft aber - oh Wunder - $@. Das übergibt alle Parameter quasi "en bloque" dem Script. Mhh: $* dürfte genauso funktionieren. Hauptsache nicht $1, sonst meint die "dusselige" 😛 Shell, nach dem ersten Leerzeichen im Dateinamen endete jener …

Du solltest Dich wirklich intensiver mit der Shell, Parameterübergabe und Quoting beschäftigen.

Quoting geht nicht, dazu müsste ich in die Parameterübergabe des Windows-Programms eingreifen können. Quoting nützte zudem auch nichts, weil dann Parameter und tatsächlicher Dateiname nicht mehr übereinstimmten. Nochmals langsam zum Mitdenken:

Die Windows Applikation legt den File unter dem Namen z:\tmp\foo bar bang.pdf (Wine sorgt durch entsprechende Konfiguration dafür, daß er in /tmp/foo bar bang.pdf landet) ab und übergibt genau diesen Namen als Parameter ans Script. Die müssen nur übereinstimmen, was in einem einzigen Fall seltsamerweise nicht gegeben war. Also gar kein Leerzeichenproblem, wie ich es Anfangs vermutete.

Ist hier vielleicht bitteschön jemand, der mehr Ahnung von der bash als wir beide zusammen hat und der "richten" ☺ mag?

Vielleicht hat jemand anderes mehr Erfolg.

Dir zu erklären, daß ein $@ ausreicht, um einen Filenamen, der Leerzeichen enthält, komplett an das Script zu übergeben?

Dir zu erklären, daß es reicht, diesen Parameter dann mit " umschlossen an den evince zu übergeben?

… ?

JUNG: ES FUNKTIONIERT DOCH JETZT !!! Was hampelst Du noch rum?

Antworten |