แปลง symlink แบบสัมบูรณ์เป็น symlink แบบสัมพัทธ์ด้วยคำสั่ง Linux อย่างง่าย


29

ฉันมีความสมบูรณ์ย่อยระบบแฟ้มภายในเส้นทาง/home/user/systemที่มีโครงสร้างลินุกซ์มาตรฐานกับไดเรกทอรี/bin, /home, /root, /usr, /var, /etc...

ระบบไฟล์ย่อยนี้มีลิงก์สัญลักษณ์ทั้งแบบสัมพันธ์หรือแบบสัมบูรณ์ symlinks /home/user/systemญาติเป็นเพียงที่ดีที่พวกเขาอยู่ในที่อยู่ภายใต้ระบบแฟ้มย่อย แต่ symlink แบบสัมบูรณ์เป็นปัญหาเนื่องจากมันชี้ไปที่เป้าหมายนอกระบบย่อย

ดังตัวอย่างเราถือว่า symlink สัมบูรณ์ดังต่อไปนี้ (เห็นในระบบย่อย):

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

ในระบบไฟล์โดยรวมเรามีลิงค์ที่/home/user/system/usr/file1ตอนนี้ชี้ไปที่ไฟล์/usr/lib/file1นอกระบบย่อยแทนที่จะเป็นไฟล์/home/user/system/usr/lib/file1 ภายในระบบย่อย

ฉันต้องการมีสคริปต์ง่ายๆโดยเฉพาะอย่างยิ่งบรรทัดคำสั่งเดียว (rsync, chroot, find, ... ) ที่แปลง symlink สัมบูรณ์ทุกรายการให้เป็นแบบสัมพัทธ์

ในตัวอย่างที่กำหนดลิงก์นั้นจะกลายเป็นลิงค์

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

4
สำหรับผู้ที่สนใจในการพยายามที่จะแก้ปัญหา Q นี้ที่นี่เป็นผู้ใช้ 250k rep'd จากความพยายามของ 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 …loops กว่า (มันตรงกันกับfor link in "$@"; do …) ข้อผิดพลาดfindเกิดจากการพิมพ์ผิด (ฉันสามารถพิมพ์ backquotes แทนคำพูดเดียวรอบ ๆ อาร์กิวเมนต์ได้-lname)
Gilles 'หยุดความชั่วร้าย'

ตอนนี้สคริปต์ทำงาน แต่ไม่เป็นไปตามที่คาดไว้ บางทีฉันอาจไม่แม่นยำพอฉันได้อัปเดตคำถาม
อเล็กซ์

@Alex ปัญหาคืออะไร? ฉันคิดว่าหลักการเป็นเสียง แต่ฉันอาจทำผิดพลาดบางอย่าง คุณลองsymlinksไหม มันจะแก้ปัญหาของคุณ - นั่นคือสิ่งที่มันถูกออกแบบมาสำหรับ (ด้วยความรำคาญที่คุณต้องเรียกใช้มัน chrooted ( fakechrootควรทำเคล็ดลับ))
Gilles 'หยุดความชั่วร้าย'

1
ฉันขอแนะนำวิธีแก้ปัญหาโดยไม่ต้องติดตั้งอะไรเพิ่มเติม
อเล็กซ์

4

ทุบตี & coreutils บริสุทธิ์เปลี่ยน symlink เป็นญาติโดยไม่จำเป็น../ในเส้นทาง:

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แปลง symlink ในไดเรกทอรีนั้น
  • ln -fsเพื่อecho ln -fsให้แห้ง

คำอธิบาย:

  • target="$(realpath "$l")" - ค้นหาพา ธ สัมบูรณ์ไปยังเป้าหมาย symlink
  • ln -fs- สร้าง symlink ( -s) บังคับให้ ( -f) เขียนที่มีอยู่ใหม่
  • realpath -s "$l" - ค้นหาเส้นทางที่แน่นอนในการเชื่อมโยงตัวเอง
  • dirname "$(realpath -s "$l")" - ค้นหาพา ธ สัมบูรณ์ไปยังไดเร็กทอรีที่มี symlink
  • realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target" - ค้นหาเส้นทางของเป้าหมายเมื่อเทียบกับ symlink หรืออีกนัยหนึ่งคือแปลงสัมบูรณ์เป็นเส้นทางสัมพัทธ์

1
ฉันขอแนะนำให้เพิ่มส่วนifคำสั่งเพื่อข้ามลิงก์ที่เสียหาย
fuenfundachtzig

0

ตัวอย่าง bash นี้ควรทำ แบบฟอร์มแรกจะพิมพ์สิ่งที่จะทำ ที่สองจะทำมัน ฉันจะตรวจสอบผลลัพธ์อย่างระมัดระวังเพราะเป็นอันตราย - การ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 / home / user / system &&
หา -lname '/ *' |
ในขณะที่อ่าน 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>จะต้องปรับให้เข้ากับสภาพแวดล้อมที่เฉพาะเจาะจง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.