ubuntuusers.de

Dateinamen die mit "-" beginnen

Status: Gelöst | Ubuntu-Version: Ubuntu MATE 20.04 (Focal Fossa)
Antworten |

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6511

Wohnort: Hamburg

Vorgeschichte: Vor 2 Tagen war auf der Startseite von Wikipedia ein Artikel über die Dolly Sisters verlinkt. Ich hatte das Bild, so wie es ist, abgespeichert und den ersten Absatz als Kommentar eingefügt.

Ich hatte nicht bemerkt, dass der Dateiname mit einem "-" beginnt. Eines meiner Programme bietet die Möglichkeit weitere Programme aufzurufen und damit gibt es ein Problem, wenn diese Programme auf Kommandozeilenoptionen lauern.

Bei einigen Programmen kann man das Problem umgehen, indem man als letzte Option, vor dem Dateinamen "–" einfügt.

Bei meinen Programmen funktioniert das jetzt und bei Hexedit auch. Aber bei Programmen wie Exiftool beiße ich auf Granit. Dort habe ich auch in der Doku nichts gefunden. In der Konsole besteht dieses Problem auch.

Bei Exiftool ist das besonders nervig, weil ich damit überprüfen will, ob meine Programme richtig funktioniert haben. Wäre schön, wenn jemand eine Idee hat, was ich da machen kann (Datei umbenennen ist ja keine echte Lösung).

Im Anhang mal ein Beispiel, wie der Aufruf passiert. Das wird dann mit fork() und execvp() umgesetzt.

Bilder

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17622

Wohnort: Berlin

Bei mir funktioniert

1
exiftool -- -test.jpg

problemlos.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11265

Wohnort: München

exiftool kann den Inhalt der Datei auch auf stdin entgegen nehmen - in der Shell sähe das z.B. so aus:

$ cat -- --test.jpg | exiftool  -
ExifTool Version Number         : 12.30
File Size                       : 0 bytes
File Modification Date/Time     : 2022:01:11 20:39:13+01:00
File Access Date/Time           : 2022:01:11 20:39:13+01:00
File Inode Change Date/Time     : 2022:01:11 20:39:13+01:00
File Permissions                : prw-------
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Image Width                     : 2048
Image Height                    : 1151
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 2048x1151
Megapixels                      : 2.4 

Mit Python3 geht das z.B. auch leicht:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> import subprocess
>>> with open('--test.jpg', 'rb') as f:
...   s = subprocess.run(['exiftool', '-'], stdin=f)
...
ExifTool Version Number         : 12.30
File Size                       : 390 KiB
File Modification Date/Time     : 2022:01:11 20:38:46+01:00
File Access Date/Time           : 2022:01:11 20:38:59+01:00
File Inode Change Date/Time     : 2022:01:11 20:38:46+01:00
File Permissions                : -rw-r--r--
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Image Width                     : 2048
Image Height                    : 1151
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 2048x1151
Megapixels                      : 2.4

Bei C(++) muss man wie es aussieht ein bisschen mit popen spielen: https://www.gnu.org/software/libc/manual/html_node/Pipe-to-a-Subprocess.html

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6511

Wohnort: Hamburg

user_unknown schrieb: Bei mir funktioniert ... problemlos.

Bei mir nicht:

manfred@samurai:~/Downloads$ exiftool -- -The_Dolly_Sisters-_MET_DP106353_\(cropped\).jpg 
Unknown option --
manfred@samurai:~/Downloads$ 

Das ist dann wohl ein Versionsproblem.

In der Konsole funktioniert das (mit irrsinnig langer Ausgabe):

manfred@samurai:~/Downloads$ cat -- -The_Dolly_Sisters-_MET_DP106353_\(cropped\).jpg | exiftool -

aber wenn ich das bei meinem Programm als Aufrufstring eingebe, geht das auch nicht:

xterm -hold -e cat -- %f | exiftool -
Invalid TAG name: The_Dolly_Sisters-_MET_DP106353_(cropped).jpg

Irgendwas mache ich da wohl noch falsch.

@seahawk1986 Python hilft mir da jetzt auch nicht, da ich nur C und etwas C++ spreche 😉

Und von popen() hatte ich mich in diesem Zusammenhang schon verabschiedet, wegen des quotings. deswegen auch der Hinweis auf execvp(), da verbiegt mir keine Shell etwas. Aber was muss/kann ich da noch machen?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13216

Die ganz allgemeine Lösung ist:

1
irgendein-programm ./-datei-mit-dash-vorne

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6511

Wohnort: Hamburg

In der Konsole funktioniert das. In meinem Programmaufruf jedoch nicht.

xterm -hold -e ./%f
Invalid TAG name: The_Dolly_Sisters-_MET_DP106353_(cropped).jpg

Irgendetwas läuft bei meiner Umsetzung noch schief. Da muss ich nochmal drüber nachdenken. Ist ja nicht einfach zu debuggen, da der Patient von einem anderen Programm aufgerufen wird.

Ich melde mich morgen wieder.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11265

Wohnort: München

Dakuan schrieb:

aber wenn ich das bei meinem Programm als Aufrufstring eingebe, geht das auch nicht:

xterm -hold -e cat -- %f | exiftool -
Invalid TAG name: The_Dolly_Sisters-_MET_DP106353_(cropped).jpg

Irgendwas mache ich da wohl noch falsch.

Du hast da vermutlich keine Shell-Umgebung - ohne den Code gesehen zu haben, würde ich sowas versuchen:

1
xterm -hold -e sh -c 'cat -- %f | exiftool -'

@seahawk1986 Python hilft mir da jetzt auch nicht, da ich nur C und etwas C++ spreche 😉

Python ist halt Ideal für einen Proof of Concept in ein paar Zeilen - in C(++) dauert es oft etwas länger...

Und von popen() hatte ich mich in diesem Zusammenhang schon verabschiedet, wegen des quotings. deswegen auch der Hinweis auf execvp(), da verbiegt mir keine Shell etwas. Aber was muss/kann ich da noch machen?

Hier gibt es Beispiele wie man das mit execvp macht: https://web.stanford.edu/class/archive/cs/cs110/cs110.1196/static/lectures/06-Execvp-Pipes-and-Interprocess-Communication/lecture-06-execvp-pipes-interprocess-communication.pdf

rklm schrieb:

Die ganz allgemeine Lösung ist:

1
irgendein-programm ./-datei-mit-dash-vorne

Aber sollte %f nicht ohnehin zu einem vollständigen Pfad expandiert werden (zumindest laut https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s07.html)? Wie ist das in dem Programm implementiert?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13216

Dakuan schrieb:

In der Konsole funktioniert das. In meinem Programmaufruf jedoch nicht.

xterm -hold -e ./%f

Was soll das sein? "%f" ist ja keine Shell-Variable. Wenn das ein Muster ist, das durch einen Dateinamen ersetzt wird, dann wird es ggf. schwierig, weil z.B. absolute Namen dann verunstaltet werden. In dem Fall bietet sich eher der Ansatz mit "–" an. Oder Du sorgst dafür, dass Dateinamen, die hier ersetzt werden, den Präfix "./" bekommen, sofern sie keine absoluten Pfade sind.

Ich glaube, ich brauche mehr Kontext.

kB Team-Icon

Supporter, Wikiteam
Avatar von kB

Anmeldungsdatum:
4. Oktober 2007

Beiträge: 9775

Wohnort: Münster

Dakuan schrieb:

[…] ein Beispiel, wie der Aufruf passiert

Hast Du '%f' und "%f" versucht?

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6511

Wohnort: Hamburg

Also das Voranstellen von './' sollte eigentlich immer funktionieren und die gesuchte Lösung sein. Da das in der Konsole funktioniert und nur mit meinem Programm nicht, muss ich wohl da erstmal einen Fehler suchen.

rklm schrieb:

Was soll das sein? "%f" ist ja keine Shell-Variable.

Ich dachte ich hätte das erwähnt. Mein aufrufendes Programm zerlegt die Kommandozeile und übergibt das dann an execvp(). Und damit habe ich mir wohl einen Designfehler eingehandelt, denn damit ist das './' ein eigener Parameter und wird nicht als Bestandteil des Dateipfades übergeben. Das aufgerufene Programm sieht dann ein zusätzliches Leerzeichen.

Da muss ich mal sehen, wie ich das reparieren kann.

Oder Du sorgst dafür, dass Dateinamen, die hier ersetzt werden, den Präfix "./" bekommen, sofern sie keine absoluten Pfade sind.

Das ist möglicherweise die einfachste Lösung. Kann ich das grundsätzlich so machen oder lieber optional?

Im Anhang sieht man nochmal die einzelnen Elemente, bevor der oder die Dateinamen angehängt werden.

Bilder

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6511

Wohnort: Hamburg

Ich habe das voranstellen von "./" zum Testen mal fest eingebaut. Es scheint sich kein Programm daran zu stören aber es hilft auch nicht immer. Bei Exiftool funktioniert es aber bei Hexedit geht es nur mit "–" vor dem Dateinamen.

Ich werde das also nur als zusätzliche Option einbauen.

P.s. das ist übrigens nicht der einzige merkwürdige Dateiname, der mir in letzter Zeit begegnet ist. Das Firefox Plugin "Video DownloadHelper" kann auch Dateinamen mit führendem Leerzeichen abliefern. Sorgt auch für Verwirrung.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13216

Dakuan schrieb:

Ich habe das voranstellen von "./" zum Testen mal fest eingebaut. Es scheint sich kein Programm daran zu stören aber es hilft auch nicht immer. Bei Exiftool funktioniert es aber bei Hexedit geht es nur mit "–" vor dem Dateinamen.

Das ist verdächtig. Was macht denn Hexedit, wenn Du einen Dateinamen, der mit "./-" anfängt, auf der Kommandozeile übergibst, ohne vorher "–" zu haben?

P.s. das ist übrigens nicht der einzige merkwürdige Dateiname, der mir in letzter Zeit begegnet ist. Das Firefox Plugin "Video DownloadHelper" kann auch Dateinamen mit führendem Leerzeichen abliefern. Sorgt auch für Verwirrung.

Ist aber völlig legal bei einem Unix-Dateisystem.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17622

Wohnort: Berlin

Dakuan schrieb:

rklm schrieb:

Was soll das sein? "%f" ist ja keine Shell-Variable.

Ich dachte ich hätte das erwähnt. Mein aufrufendes Programm zerlegt die Kommandozeile und übergibt das dann an execvp(). Und damit habe ich mir wohl einen Designfehler eingehandelt, denn damit ist das './' ein eigener Parameter und wird nicht als Bestandteil des Dateipfades übergeben. Das aufgerufene Programm sieht dann ein zusätzliches Leerzeichen.

Nicht ganz. In der Shell benutzt Du Leerzeichen (in der Regel), um die einzelnen Elemente eines Kommandos,

1
PROGRAMM [OPTION]... [ARGUMENT]... 

voneinander abzugrenzen. Wenn ein Argument ein oder mehr Leerzeichen enthält, musst Du es maskieren, damit es die Shell nicht in 2 oder mehr Teile zerlegt, die das Programm als getrennte Elemente empfängt.

1
cat file1 file2

gibt 2 Dateien aus, file1 und file2, aber

1
cat "file1 file2"

gibt eine aus, die in der Mitte ein Blank im Namen hat.

Innerhalb eines Programms, egal ob C, C++, Java, ... übergibt man aber i.d.R. ein Array (z.B. an execvp), und alle Leerzeichen müssen zum Dateinamen gehören.

Wenn Du nur eine Datei "file1 file2" hast wird der erste Befehl nicht erraten, dass es um diese geht und wenn Du nur 2 Dateien hast, file1 und file2 wird der 2. Befehl, statt nach einer Datei zu suchen nicht die 2 stattdessen nehmen.

Womöglich sind die 2 Programmaufrufe so beschaffen, dass Du einmal das "./" vor den Dateinamen geklebt hast, und das andere Mal "./" als weiteres Argument übergeben hast.

Die empfohlene Weise, das mit c++ zu machen - ein Screenshot roch nach c++ - kenne ich nicht, aber man kann in C++ Strings verketten. Hier eine sicher nicht gute Lösung (dotslash.cc):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <iostream>
#include <stdio.h>

using namespace std;

int main (int argc, char** argv) {

	char* filename = new char[256];
	sprintf (filename, "./%s", argv[1]);
	cout << filename << "\n";

	return 0;
}

Aufruf:

1
2
./dotslash -some 
./-some

Ohne den Code für hexedit und exiftool zu sehen müssen wir aber raten was Du machst, und wieso es einmal nicht klappt und das andere Mal doch.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11265

Wohnort: München

Mit asprintf wird automatisch genug Speicher alloziert:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <iostream>

int main(int argc, char *argv[]) {
    for (int arg_n=1; arg_n < argc; arg_n++) {
        char *line;
        asprintf(&line, "./%s\n", argv[arg_n]);
        std::cout << line;
        free(line);
    }
}

Bis C++20 mit std::format verfügbar ist, geht das mit fmt bequemer (ist als libfmt(-dev) in den Paketquellen):

1
2
3
4
5
6
7
#include <fmt/core.h>

int main(int argc, char *argv[]) {
    for (int arg_n=1; arg_n < argc; arg_n++) {
        fmt::print("./{}\n", argv[arg_n]);
    }
}

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6511

Wohnort: Hamburg

@rklm Ich habe da gestern wohl zu viel auf einmal ausprobiert. Ich habe den Test gerade noch einmal wiederholt und es geht auch mit Hexedit (wenn man es richtig macht).

Ist aber völlig legal bei einem Unix-Dateisystem.

Nicht alles was legal ist ist auch immer sinnvoll.

@user_unknown

... dass Du einmal das "./" vor den Dateinamen geklebt hast, und das andere Mal "./" als weiteres Argument übergeben hast.

Letzteres war die Ausgangssituation. Mein Programm erzeugt aus den Daten in der Konfiguration eine Liste der Parameter und ergänzt diese dann mit einem oder mehreren Dateinamen, die aus einer Liste kommen. Etwa so:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void
ExtProgMan::execute( const char * cmd, const std::vector<std::string>* list ) {
    ...
    // erstelle Parameterliste hier ...
    ...
    own_args = args.size();
    aix = 0;
    switch( mode ) {
        case 1: // start program for every item in list
            argv = new const char * [args.size()+2];    // +1 arg +end marker
            for( it = 0; it < args.size(); ++it )       // copy options
                argv[aix++] = args[it];
            for( it = 0; it < list->size(); ++it ) {
                //argv[aix] = (*list)[it].c_str();
                n_buf = "./";                   // patch
                n_buf += (*list)[it].c_str();   //
                argv[aix] = n_buf.c_str();      //
                argv[aix+1] = 0;
                run_it( argv ); // fork() & execvp()
            }
            break;
...

Wahrscheinlich ist es einfacher, wenn der Aufrufer den Vorspann erzeugt.

@seahawk1986 asprintf() das kannte ich noch nicht, danke.

Antworten |