![Gibt es eine Möglichkeit, das $@-Array in einem POSIX-Shell-Skript zu ändern?](https://rvso.com/image/168778/Gibt%20es%20eine%20M%C3%B6glichkeit%2C%20das%20%24%40-Array%20in%20einem%20POSIX-Shell-Skript%20zu%20%C3%A4ndern%3F.png)
Ich weiß vielleicht, wie man readlink
normale Dateien aus SymLinks erhält. Ich hatte diese sehr einfache Idee, jeden SymLink, der $@
an meine Funktion übergeben wird, wie folgt zu ersetzen:
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f "$file")
done
Ein Beispiel wäre ein SymLink /etc/os-release -> ../usr/lib/os-release
.
Aber wenn ich das mache:
set -- '/etc/os-release'; for file in "$@"; do [ -L "$file" ] && file=$(readlink -f "$file"); done; echo "$@"
Ich erhalte immer noch den ursprünglichen SymLink-Pfad, was ... enttäuschend ist. Ich hatte gehofft, ihn direkt ändern zu können. $@
Wenn das nicht möglich ist, gibt es irgendeinen Workaround?
Antwort1
Ihr Originalcode:
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f -- "$file")
done
Das Problem besteht darin, dass die Einstellung file
in der Schleife keine Auswirkungen auf den Inhalt der Liste der Positionsparameter hat.
Stattdessen können Sie verwenden
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f -- "$file")
set -- "$@" "$file"
shift
done
Nun verschieben wir die Einträge vom Anfang "$@"
nacheinander und fügen die möglicherweise geänderten Einträge am Ende der Liste hinzu. Das Ändern $@
in einer for
Schleife $@
ist kein Problem, da for
Schleifen immer über eine statische Liste iterieren, d. h. die Liste, über die iteriert wird, wird nur einmal am Anfang der Schleife ausgewertet.
Die obige Schleife setzt, wie die folgenden, bei jeder Iteration die gesamte Liste der Positionsparameter zurück. Für eineriesigListe von Dateinamen, kann dies bald ziemlich langsam werden. Wenn Ihre Shell Arrays hat,Stephen Kitts Antwortzeigt eine Möglichkeit, es zu beschleunigen.
Die Schleife könnte auch in einer etwas kürzeren Form geschrieben werden (Aufruf readlink
für jedes einzelne Element),
for dummy do
set -- "$@" "$(readlink -f -- "$1")"
shift
done
Dies wird readlink
gemäß Ihrer Frage verwendet, obwohl es sich nicht um ein POSIX-Dienstprogramm handelt.
Beachten Sie, dass wenn die Ausgabe vonreadlink
endet mit einem oder mehreren Zeilenumbrüchen(außer dem, der hinzugefügt wird, um die letzte Ausgabezeile zu beenden), würden diese entfernt. Dies ist eine Folge der Befehlsersetzung. Wenn dies ein Problem darstellt, fügen Sie der Befehlsersetzung künstlich ein abschließendes Zeichen hinzu und entfernen Sie es dann, wie mosvy
vorgeschlagenin Kommentaren:
for file do
[ -L "$file" ] && file=$(readlink -f -- "$file"; echo x)
set -- "$@" "${file%x}"
shift
done
Antwort2
Beachten Sie, dass weder readlink
noch realpath
POSIX-Befehle sind. Der POSIX-Befehlszeilen-Werkzeugkasten hat keine realpath()
-ähnliche API. Hier readlink
könnten Sie anstelle von (von dem es verschiedene inkompatible Implementierungen gibt) verwenden, zsh
das ebenfalls eine realpath()
-ähnliche Funktionalität mit seinem Modifikator integriert hat :P
. In Ihrem sh
Skript:
eval "set -- $(zsh -c 'print -r ${(qq)argv:P}' zsh "$@")"
Sie könnten Ihr Skript aber auch zsh
gleich hier eintragen, wo es nur lauten würde:
argv=($argv:P)
Oder wenn python3
es wahrscheinlicher verfügbar ist als zsh:
eval "set -- $(
LC_ALL=C python3 -c '
import sys, os, shlex
print(" ".join(map(shlex.quote, map(os.path.realpath, sys.argv[1:]))))' "$@")"