Заменить символические ссылки на файлы

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 {} +

grawity
источник
2
Спасибо за редактирования, Anon, но скрипт делает ручки имена файлов с пробелами просто отлично, цитируя переменные во всех необходимых местах. Переключение "$var"на "${var}"noop в sh / bash. Я удалил один bashism ( [[), остальное совместимо с POSIX sh.
Гравитация
Еще более надежный метод обработки сбоев символьных ссылок заключается во cpвременном файле в том же каталоге, mv -fчто и временный файл в исходном. Это следует остерегаться проблемами , как люди , удерживая Ctrl-Cили заполнение файловой системы до между rmи cpкоманды (таким образом предотвращая даже новый lnот работы). Временные имена файлов легко обнаружить ls -aдля последующей очистки, если это необходимо.
Мистер Фуз
Как примечание, я серьезно не могу вспомнить, почему я не просто использовал abstarget=$(readlink -f "$link")вместо блока case , но это могло быть причинами переносимости.
Гравитация
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 true и posix-совместимый, но не все cpреализации будут копировать каталоги.
Wyatt8740
16

Если я вас правильно понял, -Lфлаг cpкоманды должен делать именно то, что вы хотите.

Просто скопируйте все символические ссылки, и он заменит их файлами, на которые они указывают.

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

"Легко" будет функцией от вас, скорее всего.

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

Другое решение может состоять в том, чтобы смонтировать файловую систему через что-то, что скрывает ссылки sym (например, samba), а затем просто скопировать все из этого. Но во многих случаях подобное может привести к другим проблемам.

Более прямой ответ на ваш вопрос, вероятно, «да».

Редактировать: согласно запросу для более конкретной информации. Согласно man-странице для find , эта команда выведет список всех файлов ссылок sym на глубину 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 --remove-destination source / $ f $ f; сделанный

Надеюсь это поможет

MastroGeppetto
источник
как насчет использования readlink: для f в *; do cp ---- remove-destination "$ (readlink $ f)" "$ f"; сделано
Диего
Да, это хорошая идея, но вы теряете контроль над операцией. Одним из ограничений моего решения является то, что оно не работает для файлов с пробелами в середине или для очень длинных списков. Вероятно, окончательное решение состоит в том, чтобы собрать вышеприведенный 'find --exec' и 'delete destination'. Кто-нибудь хочет попробовать?
MastroGeppetto
пробелы в середине следует уменьшить, поставив "$ f" в двойных кавычках. Вот как я использую его с «find & xargs» вместо «for loop»: find ./ -type l -print0 | xargs -0 -n1 -i sh -c 'cp --remove-destination $ (readlink "{ } ")" {} "'Я разместил это как отдельный ответ для видимости. superuser.com/a/1329543/499386
Диего
2
find . -type l -exec cp --dereference --recursive '{}' '{}'.dereferenced \;

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

blahdiblah
источник
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
xaocon
источник
Небольшое улучшение поможет найти символические ссылки: для lnk в `find. -тип l`; do src = $ (readlink $ lnk) && rm $ lnk && mv $ src $ lnk; сделано (не проверено с иерархией, но будьте осторожны с обратными метками)
Roman Susi
1

У меня была такая же проблема со ссылками на копирование в gwenview, когда вы обычно ожидали, что она перейдет по ссылке и скопирует файл.

То, что я сделал, чтобы «очистить» каталог, перейдя по всем ссылкам и скопировав все указанные файлы в каталог, - это создать «чистый» каталог, запустить, например,

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

это слишком опасно для сценария, так как нет проверки, но это решило мою проблему.

user225929
источник
1
for f in *; do cp --remove-destination $(readlink "$f") "$f"; done
Лорем Ипсум Долор
источник
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
Autotrader
источник