• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

[gelöst] bash: dirname mit Blanks

Ich versuche gerade ein Script so zu ändern dass es mit Dateinamen die Leerzeichen enthalten zurechtkommt. Dabei stosse ich auf folgendes Problem:
Code:
cmd="$DIRNAME $src"
[ $opt_debug ] && echo "DEBUG cmd: $cmd"
workdir=`$cmd`
$DIRNAME ist dabei /usr/bin/dirname, $src der Name der Datei. Damit das Skript auch mit Leerzeichen in $src zurechtkommt wird der Name in einfache Anführungszeichen gefasst, also
Code:
'/path/to/file with blanks.txt'

Trotzdem funktioniert das nicht, dirname meldet einen Fehler, in obigem Beispiel wäre das
Code:
/usr/bin/dirname: extra operand `with'
Try `/usr/bin/dirname --help' for more information.

Die Debug Ausgabe lautet:
Code:
DEBUG cmd: /usr/bin/dirname '/path/to/file with blanks.txt'

Und wenn ich diesen Aufruf von dirname in einer bash eingebe bekomme ich _keinen_ Fehler.

Wie bekomme ich den Fehler weg?
 
Hier wird $src nicht als "ein Stück" abgelegt.
Damit werden $DIRNAME mehrere Argumente übergeben:
cmd="$DIRNAME $src"
Weiterhin fehlt bei workdir=`$cmd` das " um die `

Ansonsten kannst du einfach in Bash folgendes nehmen:
workdir="${src%/*}"
 
Wenn ich viel mit Dateinamen mit Leerzeichen zu tun habe, schreibe ich lieber in Perl. Da ist das kein Problem.
 

framp

Moderator
Teammitglied
panamajo schrieb:
haveaniceday schrieb:
Weiterhin fehlt bei workdir=`$cmd` das " um die `
Ok das ergibt ein anderes Verhalten. Aber `$cmd` vs. `"$cmd"` WTF?
Ist dann wohl so...
Vielleicht klärt uns ja 'habeinenschönentag' auf :roll: . Ansonsten stimme ich abdgf bei - in Python hat man das Problem nicht (Ich weiss ... das hilft Dir nicht ... aber vielleicht findest Du mal die Zeit Dich in Python einzuarbeiten ;) )
 
Code:
cmd="$DIRNAME $src"
Wenn $src Leerzeichen enthält, gibt es eine unterschiedliche Anzahl an Argumenten.
Folgendes ist unterschiedlich:
dirname /aaa /bbb/file
dirname '/aaa /bbb/file' (identisch zu dirname "/aaa /bbb/file" )

Damit bei der Interpretation von $cmd alles richtig läuft, muss das Argument $src, wegen der Leerzeichen,
entsprechend mit ' oder " eingeschlossen sein.

Das gleich gilt später für das Ergebnis, wenn es in workdir soll.
workdir="`$cmd`"
Ansonsten würde bei dem Beispiel oben (Nach Ausführung `$cmd`):
workdir=/aaa /bbb
statt richtigerweise:
workdir="/aaa /bbb"

Perl und Python sollten das gleich Problem haben, wenn die Argumente nicht über " " oder ' ' als ein Argument übergeben werden.

Wobei workdir="${src%/*}" ist der einfachste Weg, den ivh mir zur Zeit vorstellen kann.

Frohe Ostern!
 
haveaniceday schrieb:
Perl und Python sollten das gleich Problem haben, wenn die Argumente nicht über " " oder ' ' als ein Argument übergeben werden.
Nee, ist kein Problem:
Code:
#!/usr/bin/perl

use warnings;
use strict;

my $DIRNAME = "/usr/bin/dirname";
my $src = "'/path/to/file with blanks.txt'";
my $cmd = "$DIRNAME $src";

print "DEBUG cmd: $cmd\n";

# $cmd ist sogar als Shell-Befehl ausführbar:
my $workdir = `$cmd`;
print "$workdir";
(Obwohl man statt des Shell-Befehls "dirname" eher das Perl-Modul "File::Basename", verwenden würde, das eine eigene "dirname"-Funktion mitbringt.)

Python bietet sogar
Code:
a = """Komischer String"""
Damit kann man so ziemlich alles als zusammengehörig kenntlich machen.
Danke. Euch auch!
 

framp

Moderator
Teammitglied
Danke für die Erklärung haveniceday. Das sind die kleinen Nittlichkeiten der bash die man kennen muss.

Um Deine Aussage, dass man das unter Python auch machen müsste, zu testen, habe ich mal folgendes kleine Prog zusammengeschrieben:
Code:
#!/usr/bin/env python 
#-*- coding: utf-8 -*-
import subprocess 

DIRNAME='/usr/bin/dirname'
src='~/pythonWorkspace/Playground/src/spaces with spc.txt'

print "Executing %s %s" % (DIRNAME,src)
result=subprocess.check_output([DIRNAME,src])
print "Result: %s" % (result)
Das Ergebnis ist
Code:
Executing /usr/bin/dirname ~/pythonWorkspace/Playground/src/spaces with spc.txt
Result: ~/pythonWorkspace/Playground/src
Hier sind keine weiteren Quotes notwendig um mit Spaces im Dateinamen arbeiten zu können.

Frohe Ostern Euch allen !
 
A

Anonymous

Gast
abgdf schrieb:
Wenn ich viel mit Dateinamen mit Leerzeichen zu tun habe, schreibe ich lieber in Perl. Da ist das kein Problem.

So-So-So, ich sehen in deinem Perl dort oben auch nicht sehr viel weniger von den Dingern ' und " als ich in der Bash einsetzten würde wenn dort Leerzeichen zu erwarten wären ;) ;) ;)

robi
 
robi schrieb:
abgdf schrieb:
Wenn ich viel mit Dateinamen mit Leerzeichen zu tun habe, schreibe ich lieber in Perl. Da ist das kein Problem.

So-So-So, ich sehen in deinem Perl dort oben auch nicht sehr viel weniger von den Dingern ' und " als ich in der Bash einsetzten würde wenn dort Leerzeichen zu erwarten wären ;) ;) ;)

robi
In Perl gibt es eine relativ große Bandbreite von Ausdrucksmöglichkeiten. Oben habe ich einen bash-ähnlichen Stil verwendet. Außerdem wollte ich mit der Zeile
Code:
my $workdir = `$cmd`;
zeigen, daß von Perl aus auch externe Shell-Kommandos ausgeführt werden können. Die Backticks funktionieren genauso wie unter bash, es wird ein Befehl ausgeführt und dessen Ausgabe zurückgegeben.
Etwas weniger bash-artig könnte man das zum Beispiel auch so schreiben:
Code:
#!/usr/bin/perl

use warnings;
use strict;

use File::Basename 'dirname';

my $DIRNAME = "/usr/bin/dirname";
my $src = "/path/to/file with blanks.txt";

print "DEBUG cmd: $DIRNAME '$src'\n";

my $workdir = dirname $src;
print "$workdir\n";
Die Shell verwendet das Leerzeichen, um die Befehle und Optionen voneinander zu trennen. Das kann eben zu Problemen führen, wenn man versucht, der Shell zu erklären, daß man das Leerzeichen in anderer Weise verwenden möchte.
Dieses Problem hat man in Perl nicht so sehr, da das Leerzeichen normalerweise einfach nur Whitespace ist. Insofern sieht der Code zwar ähnlich aus, aber Perl fühlt sich schon etwas anders an als bash.
Dennoch kann Perl auch sehr kryptisch sein, klar.
 

framp

Moderator
Teammitglied
abgdf schrieb:
...
Code:
print "DEBUG cmd: $DIRNAME '$src'\n";
Dort ist der Dateiname auch wieder in Quotes ;) .

Ich habe mich noch ein wenig weiter schlau gemacht und dabei folgendes rausbekommen:
Code:
result=subprocess.check_output([DIRNAME,src],shell=True)
liefert genau dieselbe Fehlermeldung die der TE gemeldet hat. Der Grund liegt einfach darin, dass mit shell=True alle Parameter konkateniert werden und dann der bash zum Frass vorgeworfen werden. Dann fängt die bash an die Spaces als TokenSeparatoren zu interpretieren und dann haben wir den Salat :roll:
Wenn shell=True nicht benutzt wird, wird von Python das normale exec Interface
man exec schrieb:
EXEC(3) Linux Programmer's Manual EXEC(3)

NAME
execl, execlp, execle, execv, execvp - execute a file

SYNOPSIS
#include <unistd.h>

extern char **environ;

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
benutzt. Da ist jeder Parameter ein Arrayelement und wird NICHT von der bash interpretiert. D.h. genaugenommen liegt es nicht an der Programmiersprache, sondern an den Möglichkeiten, direkt ein Program per exec aufzurufen unter Umgehung des bash Interpreters. Ich könnte mir Vorstellen, dass sowas auch unter perl geht.

Ich ziehe daraus die Lehre, dass, wenn ich mal wieder ein bash Script erstelle, bash Variablen immer mit "" oder '' umschlossen werden. Wenn dann da wieder " oder ' drin sind ... :???: Oder man baut einen Regex, wo alle bash Metazeichen Zeichen im String escaped werden.
 
A

Anonymous

Gast
framp schrieb:
Ich ziehe daraus die Lehre, dass, wenn ich mal wieder ein bash Script erstelle, bash Variablen immer mit "" oder '' umschlossen werden. Wenn dann da wieder " oder ' drin sind ... :???: Oder man baut einen Regex, wo alle bash Metazeichen Zeichen im String escaped werden.

Schon gesagt, aber in der Bash wird es immer ssschön kompliziert bleiben, da dort andere Programme aufgerufen werden die ihrerseits wiederum eventuell Parameter benötigen in denen auch Metazeichen enthalten die die bash selbst auch interpretieren würde. Dabei kommt es zum mehrfachen Auswerten von Teilen der Kommandozeile durch die bash. Bei jeder einzelnen Auswertung kommt es dabei zu Ersetzungen und es müssen dann eventuell Metazeichen die erst in der 3. Instanz von dem dort aufgerufenes Kommando benötig werden dann in den vorangegangen Auswertungen mehrfach vor der bash geschützt werden. Da kommen manchmal zB bei sed und awk die tollsten Geschichten dabei raus. ;)
"\\\"'\\\"" und das ist jedes einzelne Zeichen genau abgezählt und hat genau seine Bedeutung. Dafür gibt es kein Regex das sowas automatisch erstellt, Hilft nur Stufe für Stufe für Stufe ausprobieren.

oder hatte ich erst diese Woche
Code:
awk 'BEGIN {NAME = "'"$1"'"}'

Solche Probleme sollten aber in allen Interpreter Sprachen auftreten wenn verschachtelt wird.

robi
 

framp

Moderator
Teammitglied
robi schrieb:
...
Code:
awk 'BEGIN {NAME = "'"$1"'"}'
...
Dieses ist schon fast ein akademischer Fall :D . Wenn man ein wenig Tante Google befragt findet man zu dem Problem eine Menge Hits. Der natürliche TokenSeparator ist nun mal ein Space und wird in der bash benutzt ... und wer Spaces in Dateinamen benutzt muss immer damit rechnen, je nach Testaufwand des ToolProviders - Probleme zu bekommen. ... Deshalb benutze ich - just to be on the safe side - keine Spaces in Filenamen und ebensowenig Umlaute oder sonstige bash Metazeichen :roll: ... und in Python das exec Interface :D
 
A

Anonymous

Gast
framp schrieb:
Deshalb benutze ich - just to be on the safe side - keine Spaces in Filenamen und ebensowenig Umlaute oder sonstige bash Metazeichen :roll:

meinst du das macht irgend jemand freiwillig :irre: ich brauch mir nur mal meinen Rechner anschauen, zig Dateien alleine mit Zeilensprüngen von Leerzeichen, Klammern und allem möglichen druckbaren und nicht druckbaren Sonder Zeichen mal abgesehen. Zum Teil absichtlich angelegt und zum Teil durch Konsolverseher einfach so passiert. Dazu noch Dateinamen in arabisch, chinesisch, russisch ...... kann ich niemals entziffern sieht aber lustig aus. ;) Solange alles außer "/" und "\0" in Dateinamen freigegeben ist, brauch ich so was hin und wieder mal testen.

robi
 
Oben