Wie kann ich absolute Pfade bereinigen oder umgehen, die von realpath oder readlink zurückgegeben werden?

9

realpathund readlinkabsolute Pfade zurückgeben:

+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome

Ein solcher Weg ist leicht zu bewältigen. So etwas wird jedoch einige Probleme haben:

Geben Sie hier die Bildbeschreibung ein

Zum Beispiel:

Geben Sie hier die Bildbeschreibung ein

Ein Name wie dieser muss also bereinigt werden, um ihn anderen Befehlen zuzuführen. Ein Anwendungsfall könnte ungefähr so ​​aussehen:

+a@X230:~/\e[92mM@r|< $hu+'|'|_e|\|\|0rth [`-_-"]$ bacon=$(realpath pullingATerdon)
+a@X230:~$ vim $bacon 

Unnötig zu sagen, vim $baconwird nicht wie erwartet funktionieren.

Was kann ich tun, um diesen absoluten Pfad zu bereinigen, damit er mit anderen Befehlen funktioniert?

Akiva
quelle
3
vim "$bacon"
Zitieren Sie
@steeldriver Warum ist das so?
Akiva
3
Denn wenn Sie dies nicht tun, geraten Sie in Situationen wie diese :)
Terdon

Antworten:

12

Wie man das richtig macht

Zu allererst immer Ihre Variablen zitieren . Was Sie versuchen zu tun, funktioniert gut, wenn Sie es richtig zitieren:

$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon

Ich habe den seltsamen Dateinamen, den Sie gewählt haben ( obwohl ich keine Ahnung habe, warum Sie ihn gewählt haben ), aus Gründen der Konsistenz beibehalten .

Weisen wir pullingATerdonnun einer Variablen den Pfad zu und versuchen dann, die Datei zu öffnen:

$ bacon="$(realpath pullingATerdon)"
$ echo "$bacon"
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]/pullingATerdon
$ ls $bacon
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':

Das scheitert erwartungsgemäß. Aber wenn wir es jetzt richtig zitieren:

$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'

Es funktioniert wie erwartet. Und ja, Sie können den Pfad auch in einem (richtigen) Editor öffnen: emacs "$bacon"funktioniert einwandfrei . OK, so wird vimund alles andere. Ihre Wahl des Herausgebers ist zwar unglücklich, aber nicht relevant.


Warum deine gescheitert ist

Eine schnelle Möglichkeit, um zu verfolgen, was in Ihrem Fall tatsächlich passiert ist, ist die Verwendung set -x(deaktivieren Sie es erneut mit set +x), wodurch die Shell jeden Befehl druckt, den sie ausführen wird, bevor sie ausgeführt wird. Aktivieren Sie die Debugging-Meldungen der Shell mit set -x:

$ set -x
$ /bin/ls $bacon 
+ ls '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':

Das zeigt uns, dass lsmit drei separaten Argumenten ausgeführt wurde : '/home/terdon/foo/\e[92mM@r|<', '+'\''|'\''|_e|\|\|0rth'und '[`-_-"]/pullingATerdon'. Dies liegt daran, dass die Shell Wortaufteilung und Glob-Erweiterung für nicht zitierte Zeichenfolgen ausführt . In diesem Fall besteht das Problem in der Wortaufteilung, da die Shell die Leerzeichen im Pfad sah und jede durch Leerzeichen getrennte Zeichenfolge als separates Argument las.

Das mkdirBeispiel ist etwas anders, aber das liegt daran, dass Sie uns die Fehlermeldung vom zweiten Aufruf des Befehls anzeigen. Ich denke, Sie haben es einmal versucht und es dann ein zweites Mal ausgeführt, um die Ausgabe für Ihre Frage zu erhalten. Das erste Mal, als Sie es ausgeführt haben, hätte es so ausgesehen:

$ mkdir $(realpath pullingATerdon)
++ realpath pullingATerdon
+ mkdir '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
mkdir: cannot create directory ‘[`-_-"]/pullingATerdon’: No such file or directory

Auch hier wird versucht, drei Verzeichnisse zu erstellen, nicht eines, da die Wörter geteilt werden. Zunächst wurde das Verzeichnis (erfolgreich) erstellt /home/terdon/foo/\e[92mM@r|<:

$ ls -l /home/terdon/foo/
total 8
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|<'
drwxr-xr-x 3 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]'

Anschließend wurde ebenfalls erfolgreich ein Verzeichnis erstellt, das +'|'|_e|\|\|0rthin Ihrem aktuellen Verzeichnis aufgerufen wird :

$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon    0 Mar 15 00:36  pullingATerdon

Anschließend wurde versucht, das Verzeichnis zu erstellen [`-_-"]/pullingATerdon. Dies ist fehlgeschlagen, da mkdirstandardmäßig keine Unterverzeichnisse erstellt werden (dies ist möglich, wenn Sie es ausführen -p):

$ mkdir baz/bar
mkdir: cannot create directory baz/bar’: No such file or directory

Da Ihre nicht in Anführungszeichen gesetzte Zeichenfolge a enthielt /, wurde davon mkdirausgegangen, dass es sich um einen Pfad aus zwei Verzeichnissen handelt, und versucht, das oberste zu finden, und ist fehlgeschlagen.

Deshalb ist es gescheitert, aber was passiert ist, ist komplizierter. Die von Ihnen verwendete Zeichenfolge ist tatsächlich ein Shell-Glob, insbesondere ein Glob-Bereich , der mit allen Dateien im aktuellen Verzeichnis übereinstimmt, deren Name eines der 5 Zeichen `ist -, _oder ". Da Sie keine solchen Dateien in Ihrem aktuellen Verzeichnis haben, stimmt der Glob mit nichts überein und gibt sich wie das Standardverhalten in bash selbst zurück:

$ echo "[\`-_-\"]/pullingATerdon"  ## some escaping is needed here
+ echo '[`-_-"]/pullingATerdon'    ## but it echoes the right thing
[`-_-"]/pullingATerdon             ## and matches nothing, so returns itself.

Um dies zu verdeutlichen, geschieht Folgendes, wenn Sie einen Glob angeben, der zu etwas passt:

$ echo [p]*   ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*

Das nicht zitierte [p*]wird in die Liste der übereinstimmenden Dateinamen (in diesem Fall nur einer) erweitert und an diese übergeben echo. Ein weiterer Grund, warum Sie alle Dinge zitieren sollten.

Der tatsächliche Fehler, den Sie anzeigen, tritt beim zweiten Ausführen des Befehls auf und schlägt beim ersten Schritt beim Erstellen fehl /home/terdon/foo/\e[92mM@r|<, da der vorherige Aufruf dieses Verzeichnis bereits erstellt hat.


Verwenden Sie im Allgemeinen immer Shell-Globs, wenn Sie mit beliebigen Dateinamen arbeiten. Dinge wie dieses:

for file in *; do command "$file"; done

Das funktioniert für jeden Dateinamen. Egal was es enthält. In unserem obigen Beispiel hätten Sie Folgendes tun können:

emacs /home/terdon/*92mM*/pullingATerdon

Jeder Glob, der die Zieldatei eindeutig identifiziert, reicht aus. Auf diese Weise müssen Sie sich keine Gedanken über die Sonderzeichen machen und können die Shell einfach damit umgehen lassen.


Einige nützliche Referenzen:

  1. Wie kann ich Dateinamen finden und sicher behandeln, die Zeilenumbrüche, Leerzeichen oder beides enthalten? : Eine der FAQs im exzellenten Grey Cat's Wiki.

  2. Sicherheitsauswirkungen des Vergessens, eine Variable in Bash / POSIX-Shells zu zitieren : der gleiche Beitrag, auf den ich am Anfang dieser Antwort verwiesen habe. Eine großartige und sehr detaillierte Erklärung aller Dinge, die schief gehen könnten, wenn Sie Ihre Shell-Variablen nicht richtig zitieren.

  3. Warum verschluckt sich mein Shell-Skript an Leerzeichen oder anderen Sonderzeichen? : alles, was Sie schon immer über den Umgang mit beliebigen Dateinamen in der Shell wissen wollten.

  4. Wann ist eine doppelte Anführungszeichen erforderlich? : Mehr über Anführungszeichen und Variablen und insbesondere die wenigen Fälle, in denen Sie sie nicht zitieren müssen

Terdon
quelle