Ersetzen Sie symbolische Verknüpfungen durch Dateien

31

Gibt es eine einfache Möglichkeit, alle symbolischen Links durch die Datei zu ersetzen, mit der sie verknüpft sind?

Marty Trenouth
quelle
3
Welches Problem versuchen Sie zu lösen?
Daniel Beck
3
Ich habe eine Reihe symbolischer Links zu einem anderen Laufwerk. Ich muss alle Dateien von diesem Laufwerk verschieben, auf die an anderer Stelle verwiesen wird, damit ich sie entfernen kann.
Marty Trenouth

Antworten:

14

Für einige Definitionen von "einfach":

#!/bin/sh
set -e
for link; do
    test -h "$link" || continue

    dir=$(dirname "$link")
    reltarget=$(readlink "$link")
    case $reltarget in
        /*) abstarget=$reltarget;;
        *)  abstarget=$dir/$reltarget;;
    esac

    rm -fv "$link"
    cp -afv "$abstarget" "$link" || {
        # on failure, restore the symlink
        rm -rfv "$link"
        ln -sfv "$reltarget" "$link"
    }
done

Führen Sie dieses Skript mit Linknamen als Argumente aus, z. B. durch find . -type l -exec /path/tos/script {} +

Grawity
quelle
2
Vielen Dank für die Bearbeitung, anon, aber das Skript tut Griff Dateinamen mit Leerzeichen ganz gut, von Variablen in allen erforderlichen Stellen zitieren. Der Wechsel "$var"zu "${var}"ist ein Noop in sh / bash. Ich habe den einen Bashismus ( [[) entfernt, der Rest ist mit POSIX sh kompatibel.
Grawity
Eine noch robustere Methode zur Behandlung von Symlink-Fehlern besteht darin cp, eine temporäre Datei im selben Verzeichnis wie mv -fdie temporäre Datei im Original zu speichern. Dies sollte vor Problemen schützen, wie das Niederhalten von Personen Ctrl-Coder das Auffüllen des Dateisystems zwischen den Befehlen rmund cp(wodurch verhindert wird, dass auch die neuen Befehle lnfunktionieren). Temporäre Dateinamen können ls -abei Bedarf für eine spätere Bereinigung leicht erkannt werden.
Herr Fooz
Als Randnotiz kann ich mich ernsthaft nicht erinnern, warum ich nicht nur abstarget=$(readlink -f "$link")den Case- Block verwendet habe, sondern möglicherweise auch aus Gründen der Portabilität.
Grawity
17

Könnte einfacher sein, einfach tar zu verwenden, um die Daten in ein neues Verzeichnis zu kopieren.

-H      (c and r mode only) Symbolic links named on the command line will
        be followed; the target of the link will be archived, not the
        link itself.

Sie könnten so etwas gebrauchen

tar -hcf - sourcedir | tar -xf - -C newdir

tar --help:
-H, --format=FORMAT        create archive of the given format
-h, --dereference          follow symlinks; archive and dump the files they point to
Steve
quelle
1
Ich mag Ihre Lösung, aber ich verstehe den letzten Teil Ihrer Botschaft nicht. Für mich sollten Sie tun:tar -hcf - sourcedir | tar -xf - -C newdir
Seb DA ROCHA
1
Einverstanden, diese Antwort müsste laut Kommentar aktualisiert werden.
Astabada
1
cp -List die einfachere Lösung
Plesiv
@plesiv true und posix-konform, aber nicht alle cpImplementierungen kopieren Verzeichnisse.
Wyatt8740
16

Wenn ich Sie richtig verstanden habe, sollte das -LFlag des cpBefehls genau das tun, was Sie wollen.

Kopieren Sie einfach alle Symlinks und sie werden durch die Dateien ersetzt, auf die sie zeigen.

RedX
quelle
4
Danke - fast das, was ich brauchte: Ich musste etwas tun wie: cp -L files tmp/ && rm files && cp tmp/files . Wenn Sie klarstellen, werden Sie wahrscheinlich mehr Menschen helfen ...
Salbei
5

"easy" wird höchstwahrscheinlich eine Funktion von Ihnen sein.

Ich würde wahrscheinlich ein Skript schreiben, das das Befehlszeilendienstprogramm "find" verwendet, um symbolisch verknüpfte Dateien zu finden, und dann rm und cp aufrufen, um die Datei zu entfernen und zu ersetzen. Es ist wahrscheinlich eine gute Idee, die Aktion von find überprüfen zu lassen, ob noch genügend freier Speicherplatz vorhanden ist, bevor Sie auch den Sym-Link verschieben.

Eine andere Lösung könnte darin bestehen, das fragliche Dateisystem durch etwas zu mounten, das die Sym-Links verbirgt (wie Samba), und dann einfach alles daraus zu kopieren. Aber in vielen Fällen würde so etwas andere Probleme mit sich bringen.

Eine direktere Antwort auf Ihre Frage lautet wahrscheinlich "Ja".

Bearbeiten: Gemäß der Anfrage für genauere Informationen. Laut der man-Seite für find listet dieser Befehl alle sym link-Dateien in einer Tiefe von 2 Verzeichnissen auf, von /:

find / -maxdepth 2 -type l -print

( dies wurde hier gefunden )

Um find zu bekommen, führen Sie etwas aus:

find / -maxdepth 2 -type l -exec ./ReplaceSymLink.sh {} \;

Ich glaube, das ruft ein Skript auf, das ich gerade erstellt habe, und übergibt den Dateinamen, den Sie gerade gefunden haben. Alternativ können Sie die Ausgabe der Suche in eine Datei erfassen (verwenden Sie "find [blah]> symlinks.data" usw.) und diese Datei dann an ein Skript übergeben, das Sie geschrieben haben, um ordnungsgemäß über das Original zu kopieren.

James T Snell
quelle
? Ein bisschen generisch - können Sie das näher erläutern?
Linker3000
2
Die Syntax lautet-exec ./ReplaceSymLink.sh {} \;
grawity
Hier finden Sie die Artikel und der als Antwort markierte wird verwendet, um die Links zu ersetzen!
Marty Trenouth
3

Dies ist der von mir verwendete Oneliner: Nehmen wir an, dass alle lokalen Dateien Links zu einer anderen Datei in "source" sind. Sie können die Dateiauswahl mit Mustern oder "Suchen" verfeinern. Bitte vor Gebrauch verstehen.

für f in *; do cp --remove-destination source / $ f $ f; getan

Hoffe das hilft

MastroGeppetto
quelle
wie wäre es mit readlink: für f in *; do cp ---- remove-destination "$ (readlink $ f)" "$ f"; erledigt
Diego
Ja, das ist eine gute Idee, aber Sie verlieren die Kontrolle über den Vorgang. Eine Einschränkung meiner Lösung ist, dass sie nicht für Dateien mit Leerzeichen in der Mitte oder für sehr lange Listen funktioniert. Wahrscheinlich besteht die endgültige Lösung darin, den obigen Befehl 'find --exec' mit dem Befehl 'remove destination' zu kombinieren. Möchte jemand es versuchen?
MastroGeppetto
Leerzeichen in der Mitte sollten durch Anführungszeichen "$ f" ausgeglichen werden. So verwende ich es mit "find & xargs" anstelle von "for loop": find ./ -type l -print0 | xargs -0 -n1 -i sh -c 'cp --remove-destination $ (readlink "{ } ")" {} "'Ich habe es als separate Antwort für die Sichtbarkeit gepostet. superuser.com/a/1329543/499386
Diego
2
find . -type l -exec cp --dereference --recursive '{}' '{}'.dereferenced \;

Erstellt eine Kopie jeder verknüpften Datei / jedes verknüpften Ordners <filename>.dereferenced, was sicherer ist (wenn es nicht so einfach ist), als sie nur direkt zu ersetzen. Das Verschieben der kopierten Daten in die Dateinamen der Symlinks wird dem Leser als Übung überlassen.

blahdiblah
quelle
2

Unter Verwendung einiger der vorherigen Ideen ersetzt der folgende Befehl rekursiv jeden Symlink durch eine Kopie des Originals:

find . -type l -exec bash -c "echo 'Replacing {} ...';  cp -LR '{}' '{}'.dereferenced;  rm '{}';  mv '{}'.dereferenced '{}'" \;
Antonio
quelle
2
find ./ -type l -print0|xargs -0 -n1 -i sh -c 'cp --remove-destination $(readlink "{}") "{}" '

basierend auf https://superuser.com/a/1301199/499386 von MastroGeppetto

Diego
quelle
1

Es gibt viele gute Antworten, aber ich habe seitdem nach etwas Leichtem gesucht

for i in *; do link=$(readlink $i) && rm $i && mv $link $i; done
xaocon
quelle
Eine kleine Verbesserung hilft, symbolische Links zu finden: für lnk in `find. -Typ l`; do src = $ (readlink $ lnk) && rm $ lnk && mv $ src $ lnk; getan (nicht mit Hierarchie getestet, Vorsicht mit Backticks)
Roman Susi
1

Ich hatte das gleiche Problem mit dem Kopieren von Links durch gwenview, wenn Sie normalerweise erwarten würden, dass es dem Link folgt und die Datei kopiert.

Was ich getan habe, um das Verzeichnis zu 'bereinigen', indem ich allen Links gefolgt bin und alle Dateien, auf die verwiesen wurde, in das Verzeichnis kopiert habe, war ein Arbeitsverzeichnis zu erstellen, z

"for file in `ls`; do cp -L $file scratchdir/$file; done; mv scratchdir/* ./" 

Dies ist zu gefährlich, um ein Skript einzufügen, da es keine Überprüfung gibt, aber es hat mein Problem behoben.

user225929
quelle
1
for f in *; do cp --remove-destination $(readlink "$f") "$f"; done
Lorem Ipsum Dolor
quelle
0

Ich habe eine einzeilige Version gemacht, da mein Web-Hotel keine Bash-Skripte erlaubte und es für mich gut funktionierte:

before:
> find . -type l | wc -l
358

> for link in `find . -type l` ; do echo "$link : "; ls -al $link ; cp $link notalink; unlink $link; mv notalink $link ; ls -al $link; done
...

after:
> find . -type l | wc -l
0
Autotrader
quelle