Перетворити абсолютне посилання на відносне символьне посилання за допомогою простої команди 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

4
Для тих, хто зацікавлений у спробі вирішити цей Q, ось 250-типічневий репід користувач із спроби SO: stackoverflow.com/questions/2564634/… . У цьому потоці є декілька потенційних рішень, але в кожному є конкретні ситуації, з якими він має справу, та інші, які не мають.
slm

Відповіді:


20

За допомогою symlinksутиліти Марка Лорда (пропонується багатьма дистрибутивами; якщо у вас її немає, побудуйте її з джерела ):

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).
Жил "ТАК - перестань бути злим"

Тепер сценарій працює, але не так, як очікувалося. Можливо, я був недостатньо точним, я оновив питання.
Олексій

@Alex У чому проблема? Я думаю, що принцип є здоровим, але я, можливо, допустив деякі помилки кодування. Ви пробували symlinks? Це вирішило б вашу проблему - це в основному і те, для чого вона була розроблена (з прикрістю, що вам доведеться запускати її з хронікою ( фальшивка повинна робити трюк)).
Жил "SO- перестань бути злим"

1
Я наполегливо вважаю, що рішення розробляється поза коробкою, без необхідності встановлювати щось додаткове.
Олексій

4

Чистий 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" - знаходить шлях цілі відносно симпосилання, іншими словами: перетворює абсолютний у відносний шлях

1
Я б запропонував додати ifпункт пропустити непрацюючі посилання.
fuenfundachtzig

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

Окрім того, що для імен з пробілами це не вдасться, чи не все це неправильно? Це префікси абсолютного шляху?
fuenfundachtzig

0

Ось чисте рішення.

CD / домашня сторінка / користувач / система &&
знайти. -імен '/ *' |
поки читаємо l; робити
  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>, повинні бути адаптовані до конкретного середовища.

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