Замініть символьні посилання на файли


31

Чи є простий спосіб замінити всі символічні посилання файлом, на який вони посилаються?


3
Яку проблему ви намагаєтеся вирішити?
Даніель Бек

3
У мене є купа символічних посилань на інший диск. Мені потрібно перемістити всі файли з цього диска, на які посилаються в іншому місці, щоб я міг його видалити.
Марті Тренут

Відповіді:


14

Для деяких визначень поняття "легко":

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

Запустіть цей скрипт з іменами посилань у якості аргументів, наприклад, через find . -type l -exec /path/tos/script {} +


2
Дякуємо за редагування, на жаль, але сценарій справді обробляє назви файлів з пробілами просто, цитуючи змінні у всіх необхідних місцях. Зміна "$var"на "${var}"noop в sh / bash. Я видалив один башизм ( [[), решта сумісна з POSIX sh.
grawity

Ще більш надійним методом обробки несправностей символьних cpпосилань є тимчасовий файл у тому самому каталозі, а потім mv -fтимчасовий файл у оригіналі. Це повинно захищати від таких проблем, як утримування людей Ctrl-Cабо заповнення файлової системи між командами rmта cp(тим самим не дозволяючи навіть новим lnпрацювати). Тимчасові імена файлів легко помітити ls -aдля подальшої очистки, якщо потрібно.
Містер Фооз

Як бічна примітка, я серйозно не можу пригадати, чому я не просто використовував abstarget=$(readlink -f "$link")замість блоку регістру , але це, можливо, було причиною переносимості.
grawity

17

Можливо, простіше використовувати tar для копіювання даних у новий каталог.

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

Ви могли б використовувати щось подібне

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

1
Я люблю ваше рішення, але я не розумію останньої частини вашого повідомлення. Для мене слід зробити:tar -hcf - sourcedir | tar -xf - -C newdir
Seb DA ROCHA

1
Погоджено, цю відповідь потрібно буде оновити відповідно до коментаря.
астабада

1
cp -Lє більш простим рішенням
plesiv

@plesiv true та сумісні з posix, але не всі cpреалізації копіюють каталоги.
Wyatt8740

16

Якщо я правильно зрозумів, -Lпрапор cpкоманди повинен робити саме те, що ви хочете.

Просто скопіюйте всі посилання, і це замінить файли, на які вони вказують.


4
Спасибі - майже те, що мені було потрібно: мені довелося зробити щось на кшталт: cp -L files tmp/ && rm files && cp tmp/files . Якщо уточниш, ти, мабуть, допоможеш більшості людей ...
мудрець

5

"легкий" буде функцією вас, швидше за все.

Я, мабуть, напишу сценарій, який використовує утиліту командного рядка "find" для пошуку символічно пов'язаних файлів, а потім викликає rm та cp для видалення та заміни файлу. Ви, мабуть, добре також, щоб дія була викликана шляхом пошуку перевірити, чи залишилось достатньо вільного місця перед переміщенням посилання sym.

Іншим рішенням може бути встановлення відповідної файлової системи через щось, що приховує посилання sym (наприклад, samba), а потім просто скопіювати все з цього. Але у багатьох випадках щось подібне спричинило б інші проблеми.

Більш пряма відповідь на ваше запитання, ймовірно, "так".

Редагувати: Відповідно до запиту на отримання більш конкретної інформації. Відповідно до сторінки man для пошуку , ця команда буде перераховувати всі файли символьних посилань на глибину до 2 каталогів з /:

find / -maxdepth 2 -type l -print

( це було знайдено тут )

Щоб знайти пошук, щоб виконати щось, знайшовши його:

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

Я вважаю, що я зателефоную до сценарію, який я щойно створив, і передасть ім'я файлу, яке ви тільки що знайшли. Крім того, ви можете захопити вихідний файл у файл (використовуйте "find [blah]> symlinks.data" тощо), а потім передати цей файл у сценарій, написаний вами, щоб граціозно скопіювати оригінал.


? Трохи загальне - ви можете розробити?
Linker3000

2
Синтаксис-exec ./ReplaceSymLink.sh {} \;
grawity

Це перехід, щоб знайти елементи, і той, який позначений як відповідь, використовується для заміни посилань!
Марті Тренут

3

Це той, який я використав: припустимо, що всі локальні файли є посиланнями на інший файл у "джерелі". Ви можете уточнити вибір файлів за допомогою шаблонів або "знайти". Будь ласка, розумійте перед використанням.

для f в *; do cp - вихідний джерело призначення / $ f $ f; зроблено

Сподіваюся, це допомагає


як щодо використання readlink: for f in *; do cp ---- remove-target "$ (readlink $ f)" "$ f"; виконано
Дієго

Так, це гарна ідея, але ви втрачаєте деякий контроль над операцією. Одне обмеження мого рішення полягає в тому, що воно не працює для файлів з пробілами посередині або для дуже довгих списків. Можливо, остаточне рішення полягає в тому, щоб зібрати вище "find --exec" з "видалити призначення". Хтось хоче спробувати?
MastroGeppetto

пробіли в середині слід пом'якшити, додавши "$ f" у подвійні лапки. Ось як я використовую його з "find & xargs" замість "for loop": find ./ -type l -print0 | xargs -0 -n1 -i sh -c 'cp --remove-target $ (readlink "{ } ")" {} "'Я опублікував це як окрему відповідь для наочності. superuser.com/a/1329543/499386
Дієго

2
find . -type l -exec cp --dereference --recursive '{}' '{}'.dereferenced \;

Зробимо копію кожного зв'язаних файлів / папок у <filename>.dereferenced, що безпечніше (якщо менш зручно), ніж просто замінити їх безпосередньо. Переміщення скопійованих даних до імен символічних посилань залишається читачем як вправа.


2

Використовуючи деякі попередні ідеї, наступна команда рекурсивно замінить кожне символьне посилання копією оригіналу:

find . -type l -exec bash -c "echo 'Replacing {} ...';  cp -LR '{}' '{}'.dereferenced;  rm '{}';  mv '{}'.dereferenced '{}'" \;


1

Там багато хороших відповідей, але я, оскільки ви шукали щось легке

for i in *; do link=$(readlink $i) && rm $i && mv $link $i; done

Трохи вдосконалення допоможе знайти символічні посилання: для lnk в `find. -типу l`; do src = $ (readlink $ lnk) && rm $ lnk && mv $ src $ lnk; зроблено (не перевіряється ієрархією, проте будьте обережні із задніми текстами)
Роман Сузі

1

У мене була така ж проблема з gwenview при копіюванні посилань, коли ви зазвичай очікували, що вона перейде за посиланням та скопіює файл.

Що я зробив, щоб 'очистити' каталог, перейшовши по всіх посиланнях і скопіювавши всі файли, що вказували на файл, до каталогу, створив каталог нуля, запустіть, наприклад,

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

це занадто небезпечно, щоб укладати сценарій, оскільки перевірки немає, але це вирішило мою проблему.



0

Я зробив однолінійну версію, оскільки мій веб-готель не дозволяв bash-скриптів, і він добре працював для мене:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.