Chuyển đổi symlink tuyệt đối thành symlink tương đối bằng lệnh Linux đơn giản


29

Tôi có một hoàn chỉnh tiểu hệ thống tập tin bên trong một con đường /home/user/systemcó chứa các cấu trúc Linux chuẩn với danh bạ /bin, /home, /root, /usr, /var, /etc, ...

Hệ thống tập tin con này chứa các liên kết tượng trưng, ​​tương đối hoặc tuyệt đối. Các liên kết tương đối là tốt, chúng nằm trong hệ thống tập tin con bên dưới /home/user/system. Nhưng các liên kết tuyệt đối có vấn đề, vì chúng trỏ đến một mục tiêu bên ngoài hệ thống tập tin con.

Để làm ví dụ, chúng tôi giả sử một liên kết tượng trưng tuyệt đối như sau (nhìn thấy bên trong hệ thống tập tin con):

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

Trong hệ thống tệp tổng thể, hiện tại chúng ta có một liên kết /home/user/system/usr/file1trỏ đến một tệp /usr/lib/file1bên ngoài hệ thống tệp phụ, thay vì một tệp /home/user/system/usr/lib/file1 bên trong hệ thống tệp phụ.

Tôi muốn có một tập lệnh đơn giản, tốt nhất là một dòng lệnh duy nhất (rsync, chroot, find, ...) để chuyển đổi mọi liên kết tuyệt đối thành một liên kết tương đối.

Trong ví dụ đã cho, liên kết tương đối đó sẽ trở thành

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

4
Đối với những người quan tâm đến việc cố gắng giải quyết Q này, đây là một người dùng 250 nghìn từ nỗ lực của SO: stackoverflow.com/questions/2564634/ . Có một số giải pháp tiềm năng trong chủ đề đó, nhưng mỗi giải pháp đều có những tình huống cụ thể mà nó giải quyết và những giải pháp khác mà nó không có.
slm

Câu trả lời:


20

Với symlinkstiện ích của Mark Lord (được cung cấp bởi nhiều bản phân phối; nếu bạn không có nó, hãy xây dựng nó từ nguồn ):

chroot /home/user/system symlinks -cr .

Ngoài ra, trên các hệ thống có readlinklệnh và -lnamevị ngữ find(cảnh báo: mã chưa được kiểm tra):

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

Đây sẽ là một giải pháp tốt đẹp! Tuy nhiên, biểu thức _ {} +có ý nghĩa gì khi kết thúc tìm kiếm? Ngoài ra, tôi nhận được một lỗi find: paths must precede expression: kshdường như không có ý nghĩa (vì đường dẫn đi trước biểu thức ksh).
Alex

@ Alex _$0cho đoạn vỏ, và {} +được thay thế bằng danh sách các đối số mà trở thành $1, $2vv mà for link; do …vòng qua (đó là đồng nghĩa với sự for link in "$@"; do …). Lỗi từ findlà do lỗi đánh máy (bằng cách nào đó tôi đã quản lý để nhập backquote thay vì các trích dẫn đơn xung quanh đối số -lname).
Gilles 'SO- ngừng trở nên xấu xa'

Bây giờ kịch bản chạy, nhưng không như mong đợi. Có lẽ tôi không đủ chính xác, tôi đã cập nhật câu hỏi.
Alex

@Alex Vấn đề là gì? Tôi nghĩ nguyên tắc là âm thanh, nhưng tôi có thể đã mắc một số lỗi mã hóa. Bạn đã thử symlinkschưa Nó sẽ giải quyết vấn đề của bạn - về cơ bản đó là những gì nó được thiết kế cho (với sự khó chịu khi bạn phải chạy nó bị chroot ( fakechroot nên thực hiện thủ thuật)).
Gilles 'SO- ngừng trở nên xấu xa'

1
Tôi mạnh mẽ quan tâm đến một giải pháp làm việc ra khỏi hộp, mà không cần phải cài đặt thêm.
Alex

4

Bash & coreutils thuần túy, thay đổi liên kết tượng trưng thành tương đối mà không cần ../s trong đường dẫn:

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

Bạn có thể thay đổi:

  • find .để find /path/to/directorychuyển đổi symlink trong thư mục đó
  • ln -fsđể echo ln -fschạy khô

Giải trình:

  • target="$(realpath "$l")" - tìm đường dẫn tuyệt đối đến đích symlink
  • ln -fs- tạo symlink ( -s), buộc ( -f) viết lại
  • realpath -s "$l" - tìm đường dẫn tuyệt đối đến chính symlink
  • dirname "$(realpath -s "$l")" - tìm đường dẫn tuyệt đối đến thư mục chứa symlink
  • realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target" - tìm đường dẫn của mục tiêu liên quan đến symlink, nói cách khác: chuyển đổi tuyệt đối thành đường dẫn tương đối

1
Tôi đề nghị thêm một ifmệnh đề để bỏ qua các liên kết bị hỏng.
fuenfundachtzig

0

Đoạn trích bash này nên làm điều đó. Mẫu đầu tiên sẽ in những gì nó sẽ làm; thứ hai sẽ làm điều đó. Tôi sẽ xem xét cẩn thận đầu ra vì nó là phá hoại - ln -fghi đè lên liên kết hiện có.

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

Bên cạnh thực tế là điều này sẽ thất bại đối với các tên có khoảng trắng, đây có phải là cách sai không? Nó tiền tố đường dẫn tuyệt đối?
fuenfundachtzig

0

Đây là một giải pháp sh tinh khiết.

cd / nhà / người dùng / hệ thống &&
tìm thấy . -lên '/ *' |
trong khi đọc l; làm
  echo ln -sf $ (echo $ (echo $ l | sed 's | / [^ /] * | / .. | g') $ (readlink $ l) | sed 's /.....//' ) $ l
xong |
sh

0

Chuyển đổi tuyệt đối trong các liên kết tương đối được hỗ trợ bởi sshfs gắn kết các thư mục từ xa thông qua ssh.

Lệnh là: Có

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

Lệnh, đặc biệt là <placeholders>, sẽ được điều chỉnh phù hợp với môi trường cụ thể.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.