Konvertieren Sie mit einem einfachen Linux-Befehl einen absoluten Symlink in einen relativen Symlink

29

Ich habe ein komplettes Unterdateisystem in einem Pfad /home/user/systemdie Standard - Linux - Struktur mit Verzeichnissen enthalten /bin, /home, /root, /usr, /var, /etc, ...

Dieses Unterdateisystem enthält symbolische Links, entweder relativ oder absolut. Die relativen Symlinks sind in Ordnung, sie bleiben im Sub-Dateisystem unter /home/user/system. Absolute Symlinks sind jedoch problematisch, da sie auf ein Ziel außerhalb des Sub-Dateisystems verweisen.

Als Beispiel nehmen wir einen absoluten Symlink wie folgt an (innerhalb des Sub-Dateisystems):

/usr/file1 -> /usr/lib/file1

Im gesamten Dateisystem haben wir an /home/user/system/usr/file1dieser Stelle einen Link zu einer Datei /usr/lib/file1außerhalb des Sub-Dateisystems anstelle einer Datei /home/user/system/usr/lib/file1 innerhalb des Sub-Dateisystems.

Ich hätte gerne ein einfaches Skript, vorzugsweise eine einzelne Befehlszeile (rsync, chroot, find, ...), die jeden absoluten Symlink in einen relativen konvertiert.

In dem gegebenen Beispiel würde dieser relative Link werden

/usr/file1 -> ../usr/lib/file1
Alex
quelle
4
Für diejenigen, die versuchen möchten, dieses Problem zu lösen, ist hier ein 250.000-stelliger Benutzer aus SOs Versuch: stackoverflow.com/questions/2564634/… . In diesem Thread gibt es mehrere mögliche Lösungen, aber jede hat bestimmte Situationen, mit denen sie zu tun hat, und andere, mit denen sie nicht zu tun hat.
slm

Antworten:

20

Mit dem symlinksDienstprogramm von Mark Lord (das von vielen Distributionen angeboten wird; wenn Sie es nicht haben, bauen Sie es aus dem Quellcode auf ):

chroot /home/user/system symlinks -cr .

Alternativ dazu können Sie auf Systemen mit einem readlinkBefehl und einem -lnamePrädikat für find(Warnung: ungetesteter Code) Folgendes tun:

cd /home/user/system &&
find . -lname '/*' -exec ksh -c '
  for link; do
    target=$(readlink "$link")
    link=${link#./}
    root=${link//+([!\/])/..}; root=${root#/}; root=${root%..}
    rm "$link"
    ln -s "$root${target#/}" "$link"
  done
' _ {} +
Gilles 'SO - hör auf böse zu sein'
quelle
Das wäre eine schöne Lösung! Was bedeutet der Ausdruck _ {} +am Ende des Fundes? Außerdem erhalte ich einen Fehler, find: paths must precede expression: kshder nicht sinnvoll zu sein scheint (da der Pfad vor dem Ausdruck ksh steht).
Alex
@ Alex _ist $0für den Shell - Schnipsel, und {} +wird durch die Liste der Argumente ersetzt, wird $1, $2usw. , die for link; do …über Maschen (es ist gleichbedeutend mit for link in "$@"; do …). Der Fehler von findist auf einen Tippfehler zurückzuführen (ich habe es irgendwie geschafft, Anführungszeichen anstelle von einfachen Anführungszeichen um das Argument zu schreiben -lname).
Gilles 'SO - hör auf, böse zu sein'
Jetzt wird das Skript ausgeführt, aber nicht wie erwartet. Vielleicht war ich nicht präzise genug, ich habe die Frage aktualisiert.
Alex
@Alex Was ist das Problem? Ich denke, das Prinzip ist solide, aber ich habe möglicherweise einige Codierungsfehler gemacht. Hast du es versucht symlinks? Es würde Ihr Problem lösen - das ist im Grunde das, wofür es entwickelt wurde (mit dem Ärger, dass Sie es chrooted ausführen müssen ( fakechroot sollte den Trick machen)).
Gilles 'SO - hör auf, böse zu sein'
1
Ich ziehe es nachdrücklich vor, dass die Lösung sofort funktioniert, ohne dass etwas zusätzliches installiert werden muss.
Alex
4

Pure bash & coreutils, ändert Symlinks in relative ohne unnötige ../s im Pfad:

find . -type l | while read l; do
    target="$(realpath "$l")"
    ln -fs "$(realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target")" "$l"
done

Du kannst ändern:

  • find .bis find /path/to/directoryzu Symlinks in diesem Verzeichnis zu konvertieren
  • ln -fsum echo ln -fsfür einen Trockenlauf

Erläuterung:

  • target="$(realpath "$l")" - Findet den absoluten Pfad zum Symlink-Ziel
  • ln -fs- Erzeugt symlink ( -s), erzwingt ( -f) das Umschreiben bestehender
  • realpath -s "$l" - findet den absoluten Pfad zum Symlink selbst
  • dirname "$(realpath -s "$l")" - Findet den absoluten Pfad zum Verzeichnis, das den Symlink enthält
  • realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target" - findet den Pfad des Ziels relativ zu symlink, dh konvertiert den absoluten in den relativen Pfad
LUMIFAZA
quelle
1
Ich würde vorschlagen, eine ifKlausel hinzuzufügen , um defekte Links zu überspringen.
Fuenfundachtzig
0

Dieses Bash-Snippet sollte es tun. Das erste Formular gibt aus, was es tun würde. der zweite wird es tun. Ich würde die Ausgabe sorgfältig prüfen, da sie destruktiv ist - ln -fder vorhandene Link wird überschrieben.

TOP=/home/user/system
cd $TOP
find * -type l -print | while read l; do echo ln -sf $TOP$(readlink $l) $l; done
find * -type l -print | while read l; do      ln -sf $TOP$(readlink $l) $l; done
qnoch
quelle
Abgesehen von der Tatsache, dass dies für Namen mit Leerzeichen fehlschlägt, ist dies nicht der falsche Weg? Es steht vor dem absoluten Pfad?
Fuenfundachtzig
0

Hier ist eine reine sh-Lösung.

cd / home / benutzer / system &&
finden . -lname '/ *' |
während ich l lese; machen
  echo ln -sf $ (echo $ (echo $ l | sed 's | / [^ /] * | / .. | g') $ (readlink $ l) | sed 's /.....//' ) $ l
fertig |
Sch
Diomidis Spinellis
quelle
0

Das Umwandeln von absoluten in relative Links wird von sshfs unterstützt, das entfernte Verzeichnisse über ssh bereitstellt.

Der Befehl lautet: Dort

sudo sshfs <remote_user>@<remote_ip_address>:/ /home/<host_user>/mntpoint/ -o transform_symlinks -o allow_other

Der Befehl, insbesondere der Befehl, <placeholders>muss an die jeweilige Umgebung angepasst werden.

user260694
quelle