Bagaimana saya bisa "me-relink" banyak symlink yang rusak?

54

Saya memiliki pohon direktori yang memiliki banyak tautan simbolis ke file di bawah /home... namun, saya telah pindah /homeke /mnt/homedan memerlukan cara untuk "menautkan kembali" semua symlink. Apakah fungsi tersebut ada atau apakah saya perlu menulis skrip untuk melakukannya?

Sebagai contoh, saya memiliki sesuatu seperti berikut:

[root@trees ~]# ls -l /mnt/home/someone/something
total 4264
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 a -> /home/someone/someotherthing/a
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 b -> /home/someone/someotherthing/b
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 c -> /home/someone/someotherthing/c
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 d -> /home/someone/someotherthing/d
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 e -> /home/someone/someotherthing/e

/mnt/home/someone/something/subdir:
total 4264
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 a -> /home/someone/someotherthing/subdir/a
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 b -> /home/someone/someotherthing/subdir/b
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 c -> /home/someone/someotherthing/subdir/c
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 d -> /home/someone/someotherthing/subdir/d
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 e -> /home/someone/someotherthing/subdir/e

Saya ingin perintah yang akan menemukan semua symlink dan menghubungkan kembali ke tempat yang sama tetapi di bawah /mnt/homealih-alih/home

Apakah ada perintah seperti itu?

Josh
sumber

Jawaban:

55

Tidak ada perintah untuk menargetkan ulang tautan simbolik, yang bisa Anda lakukan adalah menghapusnya dan membuat yang lain. Dengan asumsi Anda memiliki utilitas GNU (mis. Di bawah Linux atau Cygwin yang tidak tertanam), Anda dapat menggunakan -lnameutama finduntuk mencocokkan tautan simbolik dengan targetnya, dan readlinkuntuk membaca konten tautan. Belum dicoba:

find /mnt/home/someone/something -lname '/home/someone/*' \
     -exec sh -c 'ln -snf "/mnt$(readlink "$0")" "$0"' {} \;

Akan lebih baik untuk membuat tautan simbolik ini relatif. Ada utilitas kecil yang nyaman disebut symlinks(awalnya oleh Mark Lords, sekarang dikelola oleh J. Brandt Buckley), hadir di banyak distribusi Linux. Sebelum pindah, atau setelah Anda memulihkan tautan yang valid seperti di atas, jalankan symlinks -c /mnt/home/someone/somethinguntuk mengonversi semua symlink absolut di bawah direktori yang ditentukan ke symlink relatif kecuali mereka melintasi batas sistem file.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Jangan tersinggung, ini adalah one-liner yang bagus, tetapi penggantian string Bash mungkin bisa melakukan beberapa keajaiban melalui perubahan jalur dan akan lebih mudah.
0xC0000022L
@STATUS_ACCESS_DENIED Bagaimana? Satu-satunya operasi string adalah dengan menambahkan /mntke sebuah path; Anda tidak memerlukan operasi string yang lebih menarik daripada penggabungan.
Gilles 'SO- stop being evil'
@Gilles: maaf, saya lebih memikirkan komentar Anda dengan jalur relatif. Untuk "terjemahan" yang tepat dari contoh Anda, Anda tentu saja benar.
0xC0000022L
13

Saya tahu ini tidak persis apa yang diminta penulis tetapi tampaknya mereka sudah memiliki jawaban mereka jadi saya menambahkan ini untuk orang lain seperti saya yang tersandung pada pertanyaan.

Berikut ini akan membantu jika solusi yang lebih fleksibel diperlukan seperti memiliki banyak tautan simbolik yang rusak yang dapat diperbaiki dengan mengganti bagian dari target tautan simbolik.

misalnya. Setelah perubahan nama pengguna, untuk mengganti nama pengguna lama dengan nama pengguna baru di target banyak tautan, setelah perpindahan sudah dilakukan. Buat skrip yang disebut ganti-simlink yang ditunjukkan di bawah:

#!/bin/bash
link=$1
# grab the target of the old link
target=$(readlink -- "$1")

# replace the first occurrence of oldusername with newusername in the target string
target=${target/oldusername/newusername}

# Test the link creation
echo ln -s -- "$target" "$link"

# If the above echo shows the correct commands are being issued, then uncomment the following lines and run the command again
#rm $link
#ln -s "$target" "$link"

dan menyebutnya dengan perintah berikut:

find /home/newusername/ -lname '/home/oldusername/*' -exec ~/bin/replace-simlinks {} \;

Semoga ini bisa membantu seseorang

sunting: Terima kasih Gilles untuk kickstart pada skrip ini dan tip tentang menggunakan skrip symlink untuk membuat tautan relatif.

Gerry
sumber
1
Saya menemukan solusi ini lebih baik karena menggunakan pergantian string, yang membantu dalam kasus di mana Anda harus mengubah nama folder di tengah jalan. Solusinya juga cukup mudah dimodifikasi untuk melakukan transformasi yang lebih kompleks jika diperlukan.
Gallaecio
Saya akan merekomendasikan mengutip argumen ke substitusi string, karena itu harus dilakukan untuk menggunakan garis miring, misalnya untuk jalur dalam pertanyaan OP. target=${target/"/home"/"/mnt/home"}Sangat membantu. Terima kasih.
Walter Nissen
3

Buat /homesebagai symlink ke /mnt/home, dan semua symlink yang ada akan valid lagi.

Keith Thompson
sumber
2
Pemasangan-bind sering cenderung kurang rapuh dibandingkan symlink dalam skenario di mana program menyadari symlink dan bertindak secara berbeda tergantung pada fakta ...
0xC0000022L