Archiv verlassen und diese Seite im Standarddesign anzeigen : [C++] Dateiname mit Leerzeichen ausgeben
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
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,
_
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 behavvvv‐
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,
_
...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
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.
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})
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..
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("'","'\\''")
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
Das mit dem Schalter ist eine gute Idee, dann kann ich ja einfach alle genannten Methoden implementieren.
Danke für eure Hilfe!
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.