คุณสามารถเปลี่ยนสิ่งที่ลิงก์ชี้ไปหลังจากสร้างได้หรือไม่?


122

ระบบปฏิบัติการใดมีกลไก (การเรียกระบบไม่ใช่โปรแกรมบรรทัดคำสั่ง) เพื่อเปลี่ยนชื่อพา ธ ที่อ้างอิงโดยลิงก์สัญลักษณ์ (symlink) นอกเหนือจากการยกเลิกการเชื่อมโยงระบบเก่าและสร้างขึ้นใหม่

มาตรฐาน POSIX ไม่มี Solaris 10 ไม่ MacOS X 10.5 (Leopard) ไม่ได้ (ฉันยอมรับได้ว่า AIX หรือ HP-UX ไม่ทำเช่นกันเมื่อพิจารณาจากรายการการเรียกระบบ Linux นี้ Linux ก็ไม่มีการเรียกระบบเช่นกัน)

มีอะไรที่ทำ?

(ฉันคาดหวังว่าคำตอบคือ "ไม่")


เนื่องจากการพิสูจน์ผลลบเป็นเรื่องยากเรามาจัดระเบียบคำถามกันใหม่

หากคุณทราบว่าระบบปฏิบัติการ (คล้าย Unix) บางระบบที่ไม่ได้อยู่ในรายการไม่มีการเรียกระบบสำหรับการเขียนค่าของ symlink ใหม่ (สตริงที่ส่งคืนโดยreadlink()) โดยไม่ต้องลบ symlink เก่าออกและสร้างใหม่โปรดเพิ่ม - หรือพวกเขา - ในคำตอบ


เกิดอะไรขึ้นกับการเชื่อมโยงใหม่? ทำไมไม่เพียงแค่ออกlnคำสั่ง (หรือความเท่าเทียมกันของ API) เขียนทับลิงก์เก่า คุณมีปัญหาอะไร
ล็อต

9
ตลกดี - ฉันกำลังถามว่ามีระบบเรียกให้ทำงานเขียนโปรแกรมหรือไม่และคำถามถูกทำเครื่องหมายว่า 'เป็นของไซต์อื่น'
Jonathan Leffler

3
ตลก - ไม่ชัดเจนอย่างยิ่งว่าคุณกำลังมองหาการโทรจากระบบและคุณเพิ่งแก้ไขคำถามเพื่อเพิ่มรายละเอียดนี้ แล้วคุณจะคาดหวังให้คนอื่นหักเงินก่อนที่คุณจะเขียนมันได้อย่างไร?
Pascal Thivent

2
@ S.Lott: ฉันกำลังเขียนบทความเกี่ยวกับการรักษาความปลอดภัยและลิงก์สัญลักษณ์ จนถึงจุดหนึ่งฉันยืนยันว่า "เจ้าของตัวจริงกลุ่มสิทธิ์บน symlink นั้นไม่มีสาระสำคัญ" และเหตุผลก็คือเจ้าของ symlink เท่านั้นที่สามารถลบออกและไม่เปลี่ยนค่าได้ ฉันตรวจสอบอีกครั้งว่าไม่มีวิธีอื่นใดนอกจากการลบ symlink ของการบรรลุผลของ 'การเขียนค่า symlink ใหม่' ฉันเพิกเฉยต่อการเข้าถึงดิสก์ดิบโดยตรงและแฮ็ก FS ด้วยวิธีนั้น - มันต้องใช้สิทธิ์รูทและความกังวลของฉันอยู่กับผู้ใช้ที่ไม่ใช่รูทไม่ใช่สิ่งที่รูทสามารถทำได้
Jonathan Leffler

4
@Pascal: ฉันขอโทษ - ฉันไม่รู้ว่ามันไม่ชัดเจนว่าฉันกำลังพูดถึงการโทรของระบบจนกระทั่งผู้คนออกไปสัมผัสกับสิ่งที่ฉันตั้งใจ (ซึ่งแตกต่างจากที่ฉันพูดอย่างเห็นได้ชัด) ฉันขอโทษที่ทำให้เข้าใจผิด มันไม่ได้ตั้งใจ
Jonathan Leffler

คำตอบ:


106

AFAIK ไม่คุณทำไม่ได้ คุณต้องลบออกและสร้างใหม่ จริงๆแล้วคุณสามารถเขียนทับ symlink และอัปเดตชื่อพา ธ ที่อ้างถึงได้:

$ ln -s .bashrc test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 7 2009-09-23 17:12 test -> .bashrc
$ ln -s .profile test
ln: creating symbolic link `test': File exists
$ ln -s -f .profile test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 8 2009-09-23 17:12 test -> .profile

แก้ไข : ตามที่ OP ชี้ให้เห็นในความคิดเห็นการใช้--forceตัวเลือกจะlnทำการเรียกระบบไปunlink()ก่อนหน้าsymlink()นี้ ด้านล่างผลลัพธ์ของstraceกล่อง linux ของฉันพิสูจน์ได้:

$ strace -o /tmp/output.txt ln -s -f .bash_aliases test
$ grep -C3 ^unlink /tmp/output.txt 
lstat64("test", {st_mode=S_IFLNK|0777, st_size=7, ...}) = 0
stat64(".bash_aliases", {st_mode=S_IFREG|0644, st_size=2043, ...}) = 0
symlink(".bash_aliases", "test")        = -1 EEXIST (File exists)
unlink("test")                          = 0
symlink(".bash_aliases", "test")        = 0
close(0)                                = 0
close(1)                                = 0

ดังนั้นฉันเดาว่าคำตอบสุดท้ายคือ "ไม่"

แก้ไข : สิ่งต่อไปนี้คัดลอกมาจากคำตอบของ Arto Bendikenบน unix.stackexchange.com ประมาณปี 2016

สิ่งนี้สามารถทำได้โดยใช้อะตอมrename(2)โดยการสร้าง symlink ใหม่ก่อนโดยใช้ชื่อชั่วคราวจากนั้นเขียนทับ symlink เก่าอย่างหมดจดในครั้งเดียว ตามที่หน้าคนระบุ:

หากnewpathอ้างถึงลิงก์สัญลักษณ์ลิงก์จะถูกเขียนทับ

ในเชลล์คุณจะทำสิ่งนี้ได้mv -Tดังนี้:

$ mkdir a b
$ ln -s a z
$ ln -s b z.new
$ mv -T z.new z

คุณสามารถstraceใช้คำสั่งสุดท้ายเพื่อให้แน่ใจว่าใช้rename(2)ภายใต้ประทุน:

$ strace mv -T z.new z
lstat64("z.new", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
lstat64("z", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
rename("z.new", "z")                    = 0

โปรดทราบว่าในข้างต้นทั้งสองแบบmv -Tและstraceเฉพาะสำหรับ Linux

บน FreeBSD ให้ใช้mv -hสลับกัน

หมายเหตุบรรณาธิการ:นี่คือวิธีที่ Capistrano ทำมาหลายปีแล้วนับตั้งแต่ ~ 2.15 ดูคำขอดึงนี้


2
ตัวเลือก '-f' ไม่บังคับให้ 'ln' ทำการยกเลิกการเชื่อมโยง () จากนั้นระบบ symlink () เรียกใช้หรือไม่?
Jonathan Leffler

1
มันทำ. แต่คำถามอาจถูกมองว่า "ฉันจะทำสิ่งนั้นในขั้นตอนเดียวได้อย่างไร" แล้วมันก็เดือดลงไปถึงคำจำกัดความของ "ขั้นตอน" - บรรทัดคำสั่ง? syscall?
Michael Krelin - แฮ็กเกอร์

1
ฉันเพิ่งสังเกตเห็นว่าคุณเพิ่มข้อความ syscall ในคำถามของคุณ ;-)
Michael Krelin - แฮ็กเกอร์

2
@ ปาสกาล: มันทำ ใน Solaris เอาต์พุตจาก 'truss ln -spx' และ 'truss ln -s -fpx' จะแสดงการเรียกยกเลิกการเชื่อมโยง () ก่อนการเรียก symlink () ในกรณีที่สอง (และการเรียก symlink () ที่ล้มเหลวในกรณีแรก) ฉันคาดว่าจะใช้ได้กับ Unix ส่วนใหญ่ถ้าไม่ใช่ทุกรุ่นของ Unix
Jonathan Leffler

12
ความคิดเห็น +1 ถึง @Taai - หากจัดการกับ symlink ที่ dereferences (ชี้ไปที่) ไดเร็กทอรีคุณต้องระบุ "-n" เพื่อให้แน่ใจว่าเคล็ดลับนี้ใช้ได้ผล
เก่ง

161

ใช่คุณสามารถ!

$ ln -sfn source_file_or_directory_name softlink_name

9
ขอบคุณสำหรับการตอบกลับ -fตัวเลือกที่หมายถึง 'ลบปลายทางที่มีอยู่' ก่อนที่จะสร้างใหม่ ดังนั้นคำสั่งนี้บรรลุผล แต่ด้วยการทำตามด้วยunlink(2) symlink(2)นี่ไม่ใช่สิ่งที่คำถามเดิมเกี่ยวกับ นอกจากนี้โปรดทราบว่าคำตอบที่ได้รับการยอมรับและคำตอบที่ได้รับการโหวตมากที่สุดอันดับถัดไปใช้ln -sfในการทำงาน แต่เมื่อstraceผลลัพธ์แสดงให้เห็นว่าเป็นเช่นนั้นunlink()และsymlink()เนื่องจากไม่มีการเรียกระบบให้แก้ไข symlink
Jonathan Leffler

17
ดูเหมือนว่า -n จะต้องใช้เมื่อลิงก์ดั้งเดิมไปที่ไดเร็กทอรีแทนที่จะเป็นไฟล์
นึ่ง

11
สวิตช์ 'n' มีความสำคัญ ฉันต่อสู้กับปัญหาที่ symlink ไม่ได้รับการอัปเดต แต่คำสั่งสร้างลิงก์อื่นภายในลิงก์ที่มีอยู่เนื่องจากไม่ได้ตั้งค่าตัวเลือก 'no derefencing'
Jonathan Gruber

14

ไม่จำเป็นต้องยกเลิกการเชื่อมโยง symlink เก่าอย่างชัดเจน คุณสามารถทำได้:

ln -s newtarget temp
mv temp mylink

(หรือใช้ symlink ที่เทียบเท่าและเปลี่ยนชื่อการโทร) วิธีนี้ดีกว่าการยกเลิกการลิงก์อย่างชัดเจนเนื่องจากการเปลี่ยนชื่อเป็นแบบปรมาณูดังนั้นคุณจึงมั่นใจได้ว่าลิงก์จะชี้ไปที่เป้าหมายเก่าหรือใหม่เสมอ อย่างไรก็ตามสิ่งนี้จะไม่นำไอโหนดเดิมมาใช้ซ้ำ

ในระบบไฟล์บางระบบเป้าหมายของ symlink จะถูกเก็บไว้ในตัวไอโหนด (แทนที่รายการบล็อก) หากสั้นพอ สิ่งนี้จะถูกกำหนดในเวลาที่สร้างขึ้น

เกี่ยวกับการยืนยันว่าเจ้าของและกลุ่มที่แท้จริงไม่มีสาระสำคัญsymlink (7)บน Linux กล่าวว่ามีบางกรณีที่สำคัญ:

เจ้าของและกลุ่มของลิงก์สัญลักษณ์ที่มีอยู่สามารถเปลี่ยนแปลงได้โดยใช้ lchown (2) ครั้งเดียวที่ความเป็นเจ้าของลิงก์สัญลักษณ์มีความสำคัญคือเมื่อลิงก์ถูกลบหรือเปลี่ยนชื่อในไดเร็กทอรีที่มีการตั้งค่าบิตเหนียว (ดูสถิติ (2))

การเข้าถึงครั้งสุดท้ายและการประทับเวลาแก้ไขล่าสุดของลิงก์สัญลักษณ์สามารถเปลี่ยนแปลงได้โดยใช้ utimensat (2) หรือ lutimes (3)

บน Linux จะไม่ใช้สิทธิ์ของลิงก์สัญลักษณ์ในการดำเนินการใด ๆ สิทธิ์อยู่เสมอ 0777 (อ่านเขียนและดำเนินการสำหรับหมวดหมู่ผู้ใช้ทั้งหมด) และไม่สามารถเปลี่ยนแปลงได้


@ mark4o: The Good - การอ้างอิงถึง lchown () และการเป็นเจ้าของในไดเร็กทอรี Sticky-it มีประโยชน์ - ขอบคุณ ฉันตระหนักถึง lchown () และมันไม่ได้เป็นสาระสำคัญสำหรับวิทยานิพนธ์ของฉัน ฉันยังทราบถึงไดเรกทอรีเหนียว ฉันคิดว่าการเป็นเจ้าของเกือบจะเป็นผลมาจากกฎ - ความแตกต่างและน่าจะเป็นสาเหตุที่เรียกมันออกมาคือโดยปกติคุณสามารถลบไฟล์ได้ถ้าคุณสามารถเขียนมันได้และทุกคนสามารถเขียนลงใน symlink ได้ในนามเพราะ ของสิทธิ์ 777 แต่ในกรณีนี้กฎจะแตกต่างกันเล็กน้อย
Jonathan Leffler

1
@ mark4o: ไม่ดีดังนั้น - ลำดับคำสั่งที่แสดงไม่ได้ทำสิ่งที่คุณคิดว่ามันไม่ (อย่างน้อยในสถานการณ์นี้set -x -e; mkdir junk; ( cd junk; mkdir olddir newdir; ln -s olddir mylink; ls -ilR; ln -s newdir temp; ls -ilR; mv temp mylink; ls -ilR; ); rm -fr junk) หากคุณสร้างไฟล์ oldfile และ newfile แทนไดเร็กทอรีและรันสคริปต์ที่เทียบเท่ากัน (ส่วนใหญ่เปลี่ยน olddir เป็น oldfile, newdir เป็น newfile) คุณจะได้รับเอฟเฟกต์ที่คุณคาดหวัง ความซับซ้อนพิเศษเพียงหนึ่งเดียว! ขอบคุณสำหรับการตอบรับ
Jonathan Leffler

2

เพียงคำเตือนสำหรับคำตอบที่ถูกต้องด้านบน:

การใช้ -f / --force Method มีความเสี่ยงที่จะสูญเสียไฟล์หากคุณผสมซอร์สและเป้าหมาย:

mbucher@server2:~/test$ ls -la
total 11448
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:27 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
-rw-r--r--  1 mbucher www-data 7582480 May 25 15:27 otherdata.tar.gz
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ln -s -f thesymlink otherdata.tar.gz 
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ls -la
total 4028
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:28 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
lrwxrwxrwx  1 mbucher www-data      10 May 25 15:28 otherdata.tar.gz -> thesymlink
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz

แน่นอนว่าสิ่งนี้มีจุดมุ่งหมาย แต่มักจะเกิดความผิดพลาด ดังนั้นการลบและสร้าง symlink ใหม่จึงใช้งานได้มากกว่าเล็กน้อย แต่ยังช่วยประหยัดได้อีกด้วย:

mbucher@server2:~/test$ rm thesymlink && ln -s thesymlink otherdata.tar.gz 
ln: creating symbolic link `otherdata.tar.gz': File exists

ซึ่งอย่างน้อยก็เก็บไฟล์ของฉันไว้


การใช้ตัวเลือก-fหรือ--forceเป็นสิ่งที่อันตรายอยู่เสมอ ถือว่าผู้ใช้รู้ว่าพวกเขาเกี่ยวกับอะไร มันจะอันตรายเป็นทวีคูณหากคุณใช้เป็นประจำ ( rm -fr …ทุกครั้งจะเป็นอันตราย)
Jonathan Leffler

1

จะไม่ยกเลิกการเชื่อมโยงและสร้างใหม่ในท้ายที่สุดหรือไม่?


2
ทำไมต้องยกเลิกการลิงก์ตั้งแต่แรก ทำไมไม่เขียนทับง่ายๆ
ล็อต

5
ผลลัพธ์สุทธิจะใกล้เคียงกัน - แต่เจ้าของและกลุ่มและเวลาที่แก้ไขล่าสุด (และอาจเป็นหมายเลขไอโหนด) โดยทั่วไปจะแตกต่างกัน
Jonathan Leffler

2
@ S.Lott: ระบบเรียกว่า 'เขียนทับ' อะไร? ใน POSIX ไม่มีการเรียกดังกล่าว ใน Solaris และ MacOS X ไม่มีการโทรดังกล่าว มีการเรียกร้องให้ทำเช่นนั้นบน ... Linux, AIX, HP-UX สิ่งอื่นที่ดูเหมือน Unix หรือไม่? Windows ไม่มี symlink ในลักษณะเดียวกัน AFAICT ดังนั้นจึงไม่สำคัญต่อการวิเคราะห์ของฉัน - แต่ข้อมูลเกี่ยวกับ Windows API ที่เทียบเท่าจะเป็นประโยชน์
Jonathan Leffler

@Jonathan Leffler: อืม .... ln --forceคำสั่งจะเขียนทับลิงค์ที่มีอยู่อย่างแน่นอน
ล็อต

พวกฉันคิดว่าคุณกำลังพูดด้วยจุดประสงค์ที่หลากหลาย S.Lott กำลังหารือเกี่ยวกับปฏิบัติการln (1)ในขณะที่แมตต์ข. คุยความจริงที่ว่าsymlink (2)ไม่ไม่สนับสนุนการเขียนทับ คุณพูดถูกทั้งคู่และฉันขอแนะนำว่าการดูการนำไปใช้ln (1)จะเป็นวิธีที่เป็นส่วนตัวที่สุดในการบรรลุขั้นตอนการยกเลิกการลิงก์ซ้ำ
dmckee --- อดีตผู้ดูแลลูกแมว

0

ในกรณีที่ช่วยได้: มีวิธีแก้ไข symlink โดยใช้ผู้บัญชาการเที่ยงคืน (mc) คำสั่งเมนูคือ (เป็นภาษาฝรั่งเศสบนอินเทอร์เฟซ mc ของฉัน):

Fichier / Éditer le lien symbolique

ซึ่งอาจแปลเป็น:

File / Edit symbolic link

ทางลัดคือ Cx Cs

บางทีมันอาจจะใช้ln --forceคำสั่งภายในฉันไม่รู้

ตอนนี้ฉันกำลังพยายามหาวิธีแก้ไข symlink จำนวนมากพร้อมกัน (นั่นคือวิธีที่ฉันมาถึงที่นี่)


1
คุณตรวจสอบแล้วว่า MC ทำอะไรอยู่เบื้องหลัง? ฉันจะวางเดิมพันว่าในความเป็นจริงมันunlink()ตามด้วย a symlink()เพียงเพราะนั่นคือสิ่งที่ระบบที่เหมือน Unix มีให้
Jonathan Leffler

ไม่ฉันยังไม่ได้ตรวจสอบ และใช่มันค่อนข้างเป็นไปได้ที่จะเป็นไปตามที่คุณพูดในความเป็นจริงความจริงที่ว่าฉันแก้ไขกล่องข้อความให้ความรู้สึกว่าฉันกำลังแก้ไขเป้าหมาย symlink จริง ๆ แต่ฉันอาจถูกหลอก ...
ปิแอร์

0

ในทางเทคนิคไม่มีคำสั่งในตัวเพื่อแก้ไขลิงก์สัญลักษณ์ที่มีอยู่ สามารถทำได้อย่างง่ายดายด้วยคำสั่งสั้น ๆ ไม่กี่คำ

นี่คือฟังก์ชัน bash / zshเล็กน้อยที่ฉันเขียนเพื่ออัปเดตลิงก์สัญลักษณ์ที่มีอยู่:

# -----------------------------------------
# Edit an existing symbolic link
#
# @1 = Name of symbolic link to edit
# @2 = Full destination path to update existing symlink with 
# -----------------------------------------
function edit-symlink () {
    if [ -z "$1" ]; then
        echo "Name of symbolic link you would like to edit:"
        read LINK
    else
        LINK="$1"
    fi
    LINKTMP="$LINK-tmp"
    if [ -z "$2" ]; then
        echo "Full destination path to update existing symlink with:"
        read DEST
    else
        DEST="$2"
    fi
    ln -s $DEST $LINKTMP
    rm $LINK
    mv $LINKTMP $LINK
    printf "Updated $LINK to point to new destination -> $DEST"
}

2
ขอบคุณสำหรับความพยายาม สิ่งนี้ไม่เป็นไปตามข้อกำหนดของฉันในการเรียกใช้ระบบภาษา C มีหลายวิธีในการเปลี่ยนลิงก์ตราบเท่าที่คุณมีสิทธิ์เขียนบนไดเร็กทอรีที่มีลิงก์ (ซึ่งจำเป็นต้องใช้) ไม่มีวิธีใดเลยที่ฉันรู้ในตอนนี้ในการเปลี่ยนค่าของ symlink โดยไม่ต้องทำunlink()และsymlink()ด้วยค่าใหม่ซึ่งเป็นสิ่งที่เกิดขึ้นเบื้องหลังกับโค้ดของคุณ โดยส่วนตัวฉันมีฟังก์ชั่นที่ยืนยันข้อโต้แย้งสองข้อ (หรืออาจเป็นทวีคูณ) และประกันตัวหากการร้องขอไม่ถูกต้อง นั่นเป็นมุมมองเฉพาะ
Jonathan Leffler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.