PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C++] Dateiname mit Leerzeichen ausgeben



totycro
09-05-2008, 17:18
Ich möchte in einem C++-Programm Dateinamen mit Leerzeichen auf die Konsole ausgeben und diese Ausgabe mit anderen Programmen weiterverwenden können, z.B. so:

$ ls `meinprogramm`

Im Programm ist der Dateiname als const char* vorhanden, ein Minimalbeispiel wäre folgendes:

#include <iostream>

int main()
{
const char* file = "a b";
std::cout << file << std::endl;
}

Die Ausgabe dieses Programmes wird nicht als die Datei "a b" interpretiert, sondern als die 2 Dateien "a" und "b". Wie kann ich das ändern?

ContainerDriver
09-05-2008, 20:41
Hallo.

Du könntest dein Programm mit


$ ls "`meinprogramm`"

aufrufen.

Gruß, Florian

totycro
09-05-2008, 20:56
Hallo.

Du könntest dein Programm mit


$ ls "`meinprogramm`"

aufrufen.

Gruß, Florian

Das würde nur bei einer Datei funktionieren; ich hab es vllt vergessen zu erwähnen, dass das Programm beliebig viele Dateinamen ausgeben können soll.

ContainerDriver
09-05-2008, 21:09
Okay, dann musst du die Leerzeichen (+ eventuell andere von der Shell interpretierte Sonderzeichen, bin mir da aber im Moment nicht so sicher) maskieren und dem gesamten Befehlsaufruf ein eval voranstellen, also so:


#include <iostream>

int main()
{
const char* file = "a\\\ b";
/*
Maskierung von \ mit \
Maskierung von (Space) mit \
"doppelte" Maskierung nötig wegen eval-Aufruf
*/
std::cout << file << std::endl;
}



$ eval "ls `./prog`"
. Man könnte statt der maskierten Leerzeichen auch um die einzelne Dateien Anführungszeichen ausgeben (diese müssen wiederum maskiert werden), ist denke ich einfacher umzusetzen, weil im String nicht rumgepfuscht werden muss:


[...]
std::cout << "\\\\\\\"" << file << "\\\\\\\"" << std::endl;
//Maskierung von \ in C++ nötig: aus \\\" wird \\\\\\\"
//eval "ls `./prog`" => eval "ls \"Datei 1\" \"Datei 2\""
[...]
. Ich hoffe zumindest, dass das geht ;)

Gruß, Florian

anda_skoa
09-05-2008, 22:25
Oder du gibst sie zeilenweise aus und lässt xargs daraus eine Kommmandozeilen bauen

Quasi

./prog | xargs ls

Ciao,
_

totycro
10-05-2008, 10:37
Man könnte statt der maskierten Leerzeichen auch um die einzelne Dateien Anführungszeichen ausgeben (diese müssen wiederum maskiert werden), ist denke ich einfacher umzusetzen, weil im String nicht rumgepfuscht werden muss:


[...]
std::cout << "\\\\\\\"" << file << "\\\\\\\"" << std::endl;
//Maskierung von \ in C++ nötig: aus \\\" wird \\\\\\\"
//eval "ls `./prog`" => eval "ls \"Datei 1\" \"Datei 2\""
[...]
. Ich hoffe zumindest, dass das geht ;)

Gruß, Florian
Nachdem ich jetzt ein bisschen mit eval herumgespielt habe, hat bei mir das funktioniert:

std::cout << "\"" << file << "\" ";
// eval "ls `./prog`" => eval "ls "datei 1" "datei2""

Mit dem selben C++-Code geht auch
./prog | xargs ls

In der xargs Manpage steht übrigens ein interessanter Absatz:


Because Unix filenames can contain blanks and newlines, this default behavvv&#118;‐
iour is often problematic; filenames containing blanks and/or newlines are
incorrectly processed by xargs. In these situations it is better to use the
-0 option, which prevents such problems. When using this option you will
need to ensure that the program which produces the input for xargs also uses
a null character as a separator.

Danke für eure Lösungsvorschläge bisher, ich bin noch am überlegen, welcher am komfortabelsten ist, und bin natürlich für weitere Ideen offen..

anda_skoa
10-05-2008, 13:59
Ein Vorteil von xargs, je nach erwartetem Output deines Programms, ist, dass es die maximale Länge eine Kommandozeile berücksichtig, d.h. wenn der Output zu viel wird, ruft xargs das nachgestellte Programm mehrmals mit einer ausreichend kurzen Zeile auf.

Ciao,
_

panzi
11-05-2008, 00:03
...blanks and/or newlines are incorrectly processed by xargs....
Damit sollten leerzeichen gehn (aber newlines gehn immer noch net):

./prog | while read fname; do ls "$fname"; done

totycro
11-05-2008, 08:41
Damit sollten leerzeichen gehn (aber newlines gehn immer noch net):

./prog | while read fname; do ls "$fname"; done
An so eine Lösung hab ich auch schon gedacht, es hat aber den Nachteil, dass immer einiges getippt werden muss..

Inzwischen ist mir eine ganz andere Möglichkeit eingefallen:
Aufruf:

./prog -c "ls -l"
C++-code:

// parse die -c - Option
// erstelle einen String mit dem Befehl aus der Kommandozeile
// und den Dateinamen mit \" links und rechts davon
system(commandline);

Das hat bei mir alle Tests bestanden, also denke ich bei diesem Ansatz zu bleiben.

panzi
11-05-2008, 21:00
An so eine Lösung hab ich auch schon gedacht, es hat aber den Nachteil, dass immer einiges getippt werden muss..

Inzwischen ist mir eine ganz andere Möglichkeit eingefallen:
Aufruf:

./prog -c "ls -l"
C++-code:

// parse die -c - Option
// erstelle einen String mit dem Befehl aus der Kommandozeile
// und den Dateinamen mit \" links und rechts davon
system(commandline);

Das hat bei mir alle Tests bestanden, also denke ich bei diesem Ansatz zu bleiben.Das hat aber ganz ähnliche Schwächen wie das mit read und xargs. Also was wenn ein Datename eines der folgenden Zeichen beinhaltet? " ' \

In hochsprachen wie Python, Rupy, Java etc. aber auchin libraries wie Qt gibts entsprechende Funktionen mit denen man so etwas "sicher" machen kann. Also so auf die art: callprog((const char*[]){"ls","-lh",NULL})

totycro
13-05-2008, 08:51
Das hat aber ganz ähnliche Schwächen wie das mit read und xargs. Also was wenn ein Datename eines der folgenden Zeichen beinhaltet? " ' \

In hochsprachen wie Python, Rupy, Java etc. aber auchin libraries wie Qt gibts entsprechende Funktionen mit denen man so etwas "sicher" machen kann. Also so auf die art: callprog((const char*[]){"ls","-lh",NULL})

Sind diese 3 Zeichen (" ' \) die einzigen, bei denen es nicht funktioniert? Die könnte man doch einfach escapen..

panzi
18-05-2008, 01:08
Sind diese 3 Zeichen (" ' \) die einzigen, bei denen es nicht funktioniert? Die könnte man doch einfach escapen..
je nach dem ob du ' oder " für die quotes verwendest gibts noch mit mehr ziechen probleme. bei ' gibts nur mit anderen ' probleme. also dann kann mans so machen (ungetesteter c++ code):

#include <iostream>

int system(const std::string & prog, const std::list<std::string> & args) {
std::stringstream ss;

escape(prog, ss);

for (std::list<std::string>::const_iterator it = args.begin(), end = args.end(); it != end; ++ it) {
ss << " ";
escape(*it, ss);
}

return system(ss.str().c_str());
}

void escape(const std::string & s, std::ostream & os) {
os << "'";
for (std::string::const_iterator it = s.begin(), end = s.end(); it != end; ++ it) {
if (*it == '\'') {
os << "'\\''";
}
else {
os << *it;
}
}
os << "'";
}

Hab aber schon länger nix mehr mit C++ gemacht. In python:

import os

def system(prog,*args):
return os.system('%s %s' % (escape(prog),' '.join(escape(arg) for arg in args)))

def escape(s):
return "'%s'" % s.replace("'","'\\''")

jan61
19-05-2008, 21:12
Moin,


Oder du gibst sie zeilenweise aus und lässt xargs daraus eine Kommmandozeilen bauen

oder Du gibst sie im Programm durch \0 getrennt aus und nutzt die -0-Option von xargs. Noch besser: Du machst das per Kommandozeilenoption umschaltbar. So arbeitet der find auch (Option -print0).

Jan

totycro
20-05-2008, 10:37
Das mit dem Schalter ist eine gute Idee, dann kann ich ja einfach alle genannten Methoden implementieren.

Danke für eure Hilfe!