Преобразуйте абсолютную символическую ссылку в относительную символическую ссылку с помощью простой команды Linux

29

У меня есть полный суб-файловая система внутри пути , /home/user/systemсодержащие стандартную структуру Linux с каталогами /bin, /home, /root, /usr, /var, /etc, ...

Эта субфайловая система содержит символические ссылки, относительные или абсолютные. Относительные символические ссылки просто хороши, они остаются внутри подсистемы /home/user/system. Но абсолютные символические ссылки проблематичны, так как они указывают на цель вне субфайловой системы.

В качестве примера мы предполагаем абсолютную символическую ссылку следующим образом (видно внутри подсистемы файлов):

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

В общей файловой системе у нас есть ссылка /home/user/system/usr/file1, указывающая на файл /usr/lib/file1вне субфайловой системы, а не на файл /home/user/system/usr/lib/file1 внутри субфайловой системы.

Я хотел бы иметь простой скрипт, предпочтительно единственную командную строку (rsync, chroot, find, ...), которая преобразует каждую абсолютную символическую ссылку в относительную.

В данном примере эта относительная ссылка станет

/usr/file1 -> ../usr/lib/file1
Alex
источник
4
Для тех, кто заинтересован в попытках решить эту проблему, вот вам 250 тыс. Респондентов из попытки SO: stackoverflow.com/questions/2564634/… . В этом потоке есть несколько потенциальных решений, но у каждого есть определенные ситуации, с которыми он имеет дело, и другие, которых у него нет.
SLM

Ответы:

20

С помощью symlinksутилиты от Mark Lord (предлагается во многих дистрибутивах; если у вас ее нет, соберите ее из исходного кода ):

chroot /home/user/system symlinks -cr .

Альтернативно, в системах, которые имеют readlinkкоманду и -lnameпредикат find(предупреждение: непроверенный код):

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
' _ {} +
Жиль "ТАК - перестань быть злым"
источник
Это было бы хорошим решением! Однако, что означает выражение _ {} +в конце находки? Кроме того, я получаю ошибку, find: paths must precede expression: kshкоторая, кажется, не имеет смысла (так как путь предшествует выражению ksh).
Алекс
@Alex _является $0для сниппета оболочки, и {} +заменяется список аргументов , которые становятся $1, $2и т.д. , которые for link; do …перебирают (это синоним for link in "$@"; do …). Ошибка из- findза опечатки (мне как-то удалось набрать обратные кавычки вместо одинарных кавычек вокруг аргумента -lname).
Жиль "ТАК - перестань быть злым"
Теперь скрипт выполняется, но не так, как ожидалось. Может быть, я не был достаточно точен, я обновил вопрос.
Алекс
@ Алекс В чем проблема? Я думаю, что принцип здравый, но я, возможно, допустил некоторые ошибки кодирования. Ты пробовал symlinks? Это решило бы вашу проблему - это в основном то, для чего оно было разработано (с досадой, что вы должны запускать его chroot ( fakechroot должен сделать свое дело)).
Жиль "ТАК - перестань быть злым"
1
Я решительно отстаиваю решение, работающее из коробки, без необходимости устанавливать что-то дополнительное.
Alex
4

Pure bash & coreutils, меняет символические ссылки на относительные без ненужных ../s в пути:

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

Ты можешь измениться:

  • find .чтобы find /path/to/directoryпреобразовать символические ссылки в этом каталоге
  • ln -fsв echo ln -fsтечение сухого хода

Объяснение:

  • target="$(realpath "$l")" - находит абсолютный путь к цели символической ссылки
  • ln -fs- создает symlink ( -s), заставляет ( -f) переписывать существующие
  • realpath -s "$l" - находит абсолютный путь к самой символической ссылке
  • dirname "$(realpath -s "$l")" - находит абсолютный путь к каталогу, содержащему символическую ссылку
  • realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target" - находит путь цели относительно символической ссылки, другими словами: преобразует абсолютный путь в относительный путь
LUMIFAZA
источник
1
Я бы предложил добавить ifпункт, чтобы пропустить неработающие ссылки.
fuenfundachtzig
Не работает По каждой ссылке я получаю realpath: <путь к символической ссылке>: нет такого файла или каталога
whoKnows
0

Этот фрагмент кода должен сделать это. Первая форма напечатает, что она будет делать; второй сделает это. Я бы внимательно рассмотрел вывод, поскольку он разрушительный - ln -fперезаписывает существующую ссылку.

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
qneill
источник
Помимо того, что это не удастся для имен с пробелами, разве это не наоборот? Это префикс абсолютного пути?
fuenfundachtzig
0

Вот чистое решение.

cd / home / user / system &&
находить . -lname '/ *' |
пока читаю л; делать
  echo ln -sf $ (echo $ (echo $ l | sed 's | / [^ /] * | / .. | g') $ (readlink $ l) | sed 's /.....//' ) $ l
сделано |
ш
Диомидис Спинеллис
источник
0

Преобразование абсолютного в относительные ссылки поддерживается sshfs, который монтирует удаленные каталоги через ssh.

Команда: Там

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

Команда, особенно <placeholders>, должна быть адаптирована к конкретной среде.

user260694
источник