Ubah symlink absolut menjadi symlink relatif dengan perintah Linux sederhana

29

Saya memiliki sub-filesystem lengkap dalam jalur /home/user/systemyang berisi struktur Linux standar dengan direktori /bin, /home, /root, /usr, /var, /etc, ...

Sistem sub-file ini mengandung tautan simbolik, baik relatif maupun absolut. Symlink relatif baik-baik saja, mereka tetap berada di dalam sub-filesystem di bawah /home/user/system. Tetapi symlink absolut bermasalah, karena mereka menunjuk ke target di luar sub-filesystem.

Sebagai contoh, kita mengasumsikan symlink absolut sebagai berikut (terlihat di dalam sub-filesystem):

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

Dalam keseluruhan sistem file, kami memiliki tautan /home/user/system/usr/file1yang sekarang mengarah ke file di /usr/lib/file1luar sub-sistem file, alih-alih file /home/user/system/usr/lib/file1 di dalam sub-sistem file.

Saya ingin memiliki skrip sederhana, lebih disukai baris perintah tunggal (rsync, chroot, find, ...) yang mengubah setiap symlink absolut menjadi relatif.

Dalam contoh yang diberikan, tautan relatif itu akan menjadi

/usr/file1 -> ../usr/lib/file1
Alex
sumber
4
Bagi mereka yang tertarik untuk mencoba menyelesaikan Q ini, berikut adalah pengguna 250k yang dikirim ulang dari upaya SO: stackoverflow.com/questions/2564634/… . Ada beberapa solusi potensial di utas itu, tetapi masing-masing memiliki situasi khusus yang berhubungan dengan itu dan yang lainnya tidak.
slm

Jawaban:

20

Dengan symlinksutilitas oleh Mark Lord (ditawarkan oleh banyak distribusi; jika Anda tidak memilikinya, bangunlah dari sumber ):

chroot /home/user/system symlinks -cr .

Atau, pada sistem yang memiliki readlinkperintah dan -lnamepredikat ke find(peringatan: kode yang tidak diuji):

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
' _ {} +
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Ini akan menjadi solusi yang bagus! Namun, apa arti ungkapan _ {} +di akhir penemuan? Juga, saya mendapatkan kesalahan find: paths must precede expression: kshyang sepertinya tidak masuk akal (karena path mendahului ekspresi ksh).
Alex
@ Alex _adalah $0untuk potongan shell, dan {} +digantikan oleh daftar argumen yang menjadi $1, $2, dll yang for link; do …loop atas (itu identik dengan for link in "$@"; do …). Kesalahan dari findini karena kesalahan ketik (saya entah bagaimana berhasil mengetikkan backquotes alih-alih tanda kutip tunggal di sekitar argumen untuk -lname).
Gilles 'SANGAT berhenti menjadi jahat'
Sekarang skrip berjalan, tetapi tidak seperti yang diharapkan. Mungkin saya tidak cukup tepat, saya telah memperbarui pertanyaannya.
Alex
@ Alex Apa masalahnya? Saya pikir prinsipnya bagus, tetapi saya mungkin telah membuat beberapa kesalahan pengkodean. Apakah kamu sudah mencoba symlinks? Ini akan menyelesaikan masalah Anda - pada dasarnya untuk apa ia dirancang (dengan kesal karena Anda harus menjalankannya chroot ( fakechroot harus melakukan triknya)).
Gilles 'SANGAT berhenti menjadi jahat'
1
Saya sangat perfer solusi yang bekerja di luar kotak, tanpa perlu menginstal sesuatu tambahan.
Alex
4

Bash murni & coreutils, mengubah symlink menjadi relatif tanpa ../jalur yang tidak perlu :

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

Anda dapat mengubah:

  • find .untuk find /path/to/directorymengkonversi symlink di direktori itu
  • ln -fsuntuk echo ln -fsuntuk kering run

Penjelasan:

  • target="$(realpath "$l")" - Menemukan jalur absolut ke target symlink
  • ln -fs- membuat symlink ( -s), forcing ( -f) menulis ulang yang sudah ada
  • realpath -s "$l" - Menemukan jalur absolut ke symlink itu sendiri
  • dirname "$(realpath -s "$l")" - Menemukan jalur absolut ke direktori yang berisi symlink
  • realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target" - Menemukan jalur target relatif ke symlink, dengan kata lain: mengkonversi absolut ke jalur relatif
LUMIFAZA
sumber
1
Saya sarankan untuk menambahkan ifklausa untuk melewati tautan yang rusak.
fuenfundachtzig
0

Cuplikan bash ini harus melakukannya. Formulir pertama akan mencetak apa yang akan dilakukan; yang kedua akan melakukannya. Saya akan hati-hati meninjau output karena merusak - ln -fmenimpa tautan yang ada.

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
sumber
Selain fakta bahwa ini akan gagal untuk nama dengan spasi, bukankah ini cara yang salah? Ini awalan jalur absolut?
fuenfundachtzig
0

Ini adalah solusi murni.

cd / home / pengguna / sistem &&
temukan. -lname '/ *' |
saat membaca l; melakukan
  echo ln -sf $ (echo $ (echo $ l | sed 's | / [^ /] * | / .. | g') $ (readlink $ l) | sed 's /.....//' ) $ l
selesai |
SH
Diomidis Spinellis
sumber
0

Mentransformasi absolut di tautan relatif didukung oleh sshfs yang memasang direktori jauh melalui ssh.

Perintahnya adalah: Ada

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

Perintah, terutama <placeholders>, harus disesuaikan dengan lingkungan spesifik.

pengguna260694
sumber