Zamień dowiązania symboliczne na pliki

31

Czy istnieje prosty sposób na zastąpienie wszystkich dowiązań symbolicznych plikiem, do którego prowadzą?

Marty Trenouth
źródło
3
Jaki problem próbujesz rozwiązać?
Daniel Beck
3
Mam kilka symbolicznych linków do innego dysku. Potrzebuję przenieść wszystkie pliki z tego dysku, do których istnieją odniesienia w innym miejscu, aby móc je usunąć.
Marty Trenouth

Odpowiedzi:

14

W przypadku niektórych definicji „łatwego”:

#!/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

Uruchom ten skrypt z nazwami łączy jako argumentami, np. Poprzez find . -type l -exec /path/tos/script {} +

grawitacja
źródło
2
Dzięki za edycję, anon, ale skrypt dobrze sobie radzi z nazwami plików ze spacjami, cytując zmienne we wszystkich niezbędnych miejscach. Zmiana "$var"na "${var}"to noop w sh / bash. Usunąłem jeden bashism ( [[), reszta jest kompatybilna z POSIX sh.
grawitacja
Jeszcze bardziej niezawodną metodą radzenia sobie z awariami dowiązań symbolicznych jest cputworzenie pliku tymczasowego w tym samym katalogu, a następnie mv -fpliku tymczasowego do oryginału. Powinno to zabezpieczyć się przed problemami takimi jak przytrzymywanie się ludzi Ctrl-Club zapełnianie się systemu plików między poleceniami rmi cp(zapobiegając w ten sposób działaniu nawet nowego ln). Tymczasowe nazwy plików są łatwe do wykrycia w ls -acelu późniejszego wyczyszczenia w razie potrzeby.
Pan Fooz,
Na marginesie, ja poważnie nie pamiętam, dlaczego nie wystarczy użyć abstarget=$(readlink -f "$link")zamiast przypadku bloku, ale może to być powody przenośności.
grawity
17

Może być łatwiej po prostu użyć tar do skopiowania danych do nowego katalogu.

-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.

Możesz użyć czegoś takiego

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
źródło
1
Podoba mi się twoje rozwiązanie, ale nie rozumiem ostatniej części twojego przesłania. Dla mnie powinieneś zrobić:tar -hcf - sourcedir | tar -xf - -C newdir
Seb DA ROCHA
1
Zgadzam się, ta odpowiedź będzie wymagać aktualizacji zgodnie z komentarzem.
astabada
1
cp -Ljest prostsze rozwiązanie
plesiv
@plesiv true i zgodny z POSIX-em, ale nie wszystkie cpimplementacje skopiują katalogi.
Wyatt8740,
16

Jeśli dobrze cię zrozumiałem, -Lflaga cppolecenia powinna robić dokładnie to, co chcesz.

Po prostu skopiuj wszystkie dowiązania symboliczne, a zastąpi je plikami, na które wskazują.

RedX
źródło
4
Dzięki - prawie to, czego potrzebowałem: musiałem zrobić coś takiego: cp -L files tmp/ && rm files && cp tmp/files . jeśli wyjaśnisz, prawdopodobnie pomożesz większej liczbie osób ...
mędrzec
5

najprawdopodobniej będzie to „łatwe”.

Prawdopodobnie napisałbym skrypt, który korzysta z narzędzia wiersza polecenia „znajdź”, aby znaleźć symbolicznie połączone pliki, a następnie wywołuje polecenia rm i cp w celu usunięcia i zastąpienia pliku. Prawdopodobnie dobrze by było, aby akcja została również wywołana przez find sprawdź, czy pozostało wystarczającej ilości wolnego miejsca przed przeniesieniem również łącza sym.

Innym rozwiązaniem może być zamontowanie omawianego systemu plików przez coś, co ukrywa łącza sym (jak samba), a następnie po prostu skopiuj wszystko z tego. Ale w wielu przypadkach coś takiego wprowadzałoby inne problemy.

Bardziej bezpośrednia odpowiedź na twoje pytanie to prawdopodobnie „tak”.

Edycja: zgodnie z prośbą o bardziej szczegółowe informacje. Według strony man dla find , to polecenie wyświetli wszystkie pliki dowiązań sym do głębokości 2 katalogów, z /:

find / -maxdepth 2 -type l -print

( znaleziono to tutaj )

Aby znaleźć, aby wykonać coś po znalezieniu:

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

Wierzę, że wywoła jakiś skrypt, który właśnie wymyśliłem, i przekazuje nazwę pliku, który właśnie znalazłeś. Alternatywnie, możesz przechwycić wyjście find do pliku (użyj „find [blah]> symlinks.data”, itp.), A następnie przekazać ten plik do skryptu, który napisałeś, aby z wdziękiem poradzić sobie z kopiowaniem oryginału.

James T. Snell
źródło
? Trochę ogólny - czy możesz opracować?
Linker3000,
2
Składnia jest następująca-exec ./ReplaceSymLink.sh {} \;
grawity
To jest, aby znaleźć elementy, a ten oznaczony jako odpowiedź służy do zastąpienia linków!
Marty Trenouth
3

Oto oneliner, którego użyłem: załóżmy, że wszystkie pliki lokalne są linkami do innego pliku w „źródle”. Możesz zawęzić wybór plików za pomocą wzorów lub „znaleźć”. Proszę zrozumieć przed użyciem.

dla f w *; do cp --remove-destination source / $ f $ f; gotowy

Mam nadzieję że to pomoże

MastroGeppetto
źródło
co powiesz na użycie readlink: for f in *; do cp ---- remove-destination "$ (readlink $ f)" "$ f"; zrobione
Diego
Tak, to dobry pomysł, ale tracisz kontrolę nad operacją. Jednym z ograniczeń mojego rozwiązania jest to, że nie działa ono w przypadku plików ze spacjami w środku lub w przypadku bardzo długich list. Prawdopodobnie ostatecznym rozwiązaniem jest połączenie powyższego „find --exec” z „remove destination”. Czy ktoś chce spróbować?
MastroGeppetto
puste pola pośrodku należy złagodzić, umieszczając „$ f” w podwójnych cudzysłowach. Oto jak używam go z „find & xargs” zamiast „for loop”: find ./ -type l -print0 | xargs -0 -n1 -i sh -c 'cp --remove-destination $ (readlink "{ } ")" {} "'Zamieściłem go jako osobną odpowiedź na widoczność. superuser.com/a/1329543/499386
Diego
2
find . -type l -exec cp --dereference --recursive '{}' '{}'.dereferenced \;

Zrobi kopię każdego pliku / folderu z dowiązaniem symbolicznym <filename>.dereferenced, co jest bezpieczniejsze (jeśli mniej wygodne) niż zwykłe zastąpienie ich bezpośrednio. Przeniesienie skopiowanych danych do nazw plików dowiązań symbolicznych pozostawia się jako ćwiczenie dla czytelnika.

blahdiblah
źródło
2

Korzystając z niektórych wcześniejszych pomysłów, następujące polecenie rekurencyjnie zastąpi każde dowiązanie symboliczne kopią oryginału:

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

na podstawie /superuser//a/1301199/499386 of MastroGeppetto

Diego
źródło
1

Jest tam wiele dobrych odpowiedzi, ale odkąd szukałeś czegoś łatwego

for i in *; do link=$(readlink $i) && rm $i && mv $link $i; done
xaocon
źródło
Trochę ulepszenia pomoże znaleźć dowiązania symboliczne: dla lnk w `find. -typ l`; do src = $ (readlink $ lnk) && rm $ lnk && mv $ src $ lnk; gotowe (nie testowane hierarchicznie, uważaj jednak na backtyki)
Roman Susi
1

Miałem ten sam problem z kopiowaniem linków przez gwenview, kiedy normalnie spodziewałbyś się, że podąży za linkiem i skopiuje plik.

To, co zrobiłem, aby „wyczyścić” katalog, wykonując wszystkie łącza i kopiując wszystkie wskazane pliki do katalogu, to utworzenie katalogu scratch, uruchom np.

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

jest to zbyt niebezpieczne, aby umieścić w skrypcie, ponieważ nie ma sprawdzania, ale naprawiło mój problem.

użytkownik225929
źródło
1
for f in *; do cp --remove-destination $(readlink "$f") "$f"; done
Lorem Ipsum Dolor
źródło
0

Zrobiłem wersję jednowierszową, ponieważ mój hotel nie zezwalał na skrypty bash i działało to dla mnie dobrze:

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
źródło