Perintah idempoten apa yang dapat saya gunakan untuk membuat symlink menunjuk ke direktori?

16

Saya ingin memasukkan perintah ke skrip shell yang akan membuat symlink ke direktori, tetapi skrip ini bisa dijalankan berulang-ulang, jadi pada pemanggilan berikutnya, perintah itu tidak boleh mengubah apa pun.

Berikut adalah struktur direktori:

% tree /tmp/test_symlink 
/tmp/test_symlink
├── foo
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

Saya ingin membuat symlink di foo/snippet yang disebut yang menunjuk ke direktori /tmp/test_symlink/repo/resources/snippets.
Jadi saya jalankan:

% ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/snippets
'/tmp/test_symlink/foo/snippets' -> '/tmp/test_symlink/repo/resources/snippets'

yang memberikan hasil yang diinginkan.

% tree /tmp/test_symlink                                                          
/tmp/test_symlink
├── foo
   └── snippets -> /tmp/test_symlink/repo/resources/snippets
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

5 direktori, 5 file

Namun, ketika perintah dijalankan lagi,

% ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/snippets
'/tmp/test_symlink/foo/snippets/snippets' -> '/tmp/test_symlink/repo/resources/snippets'

itu menciptakan symlink ke direktori, di mana symlink aleady menempatkan symlink di dalam direktori nyata

% tree /tmp/test_symlink                                                          
/tmp/test_symlink
├── foo
   └── snippets -> /tmp/test_symlink/repo/resources/snippets
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets -> /tmp/test_symlink/repo/resources/snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

mengapa ini terjadi dan bagaimana saya bisa memodifikasi perintah sehingga doa berikutnya tidak akan membuat efek aneh ini?

the_velour_fog
sumber

Jawaban:

16

Anda harus menggunakan -Topsi untuk ini, ia memberi tahu lnuntuk selalu memperlakukan nama tautan sebagai nama tautan yang diinginkan, tidak pernah sebagai direktori.

Tanpa opsi ini, jika Anda memberikan lnnama tautan yang ada sebagai direktori, itu membuat tautan baru ke target di dalam direktori itu.

Perhatikan bahwa itu -Tadalah GNU-isme (setidaknya, itu tidak ada dalam POSIX), tetapi Anda sudah menggunakan -vGNU-ism juga, jadi saya kira itu bukan masalah.

Atau, Anda bisa menentukan direktori induk sebagai nama tautan, dan tautan itu akan selalu dibuat (kembali) di sana:

ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/

Ini berfungsi karena symlink Anda memiliki nama yang sama dengan target.

Stephen Kitt
sumber
terima kasih, ya opsi gnu belum tentu portabel, tetapi mereka bisa sangat berguna - ketika portabilitas bukan keharusan. jawaban Anda cocok untuk saya. Dari halaman manual -n, --no-dereference treat LINK_NAME as a normal file if it is a symbolic link to a directory. -T, --no-target-directory treat LINK_NAME as a normal file alwaysMenurut Anda apakah lebih baik memperlakukan symlink sebagai file selalu? Saya akan berpikir akan lebih baik untuk membatasi penggunaan opsi "khusus" ini?
the_velour_fog
Jika Anda tahu mereka tersedia, tidak ada alasan untuk menghindari opsi khusus-GNU (IMO). Anda meminta perintah idempoten, -Tadalah bagaimana Anda membuat lnidempoten. Ada cara lain untuk mendapatkan hasil yang sama jika Anda mau, seperti menghapus tautan dan membuatnya kembali, atau jika Anda sudah tahu foo, ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/(sebenarnya itu mungkin jawaban yang lebih baik sama sekali, saya akan memperbarui).
Stephen Kitt
1
keren, ya saya sebelumnya telah menggunakan ifpernyataan untuk mendeteksi keberadaan symlink dan melewatkan perintah jika symlink ada, tetapi skrip akhirnya menjadi berantakan dan sulit dibaca, saya mencari sesuatu yang idempoten tetapi sederhana, seperti mkdir -ptidak menggunakan tes , tidak ada logika, hanya bagus satu baris :)
the_velour_fog
@Stephen Kitt: Anda menyebutkan penggunaan -T, tapi saya tidak bisa menemukannya di cuplikan kode jawaban Anda. Apakah saya salah paham atau melewatkan sesuatu?
Guizmoo03
@ Guizmoo03 mungkin Anda melewatkan "Atau" yang memperkenalkan potongan. Tiga paragraf pertama menjelaskan -T, tetapi akhir jawaban saya menyajikan solusi lain yang tidak digunakan -T.
Stephen Kitt
-1

Terbaik yang bisa saya katakan apa yang terjadi di sini adalah bahwa pada pass kedua lnperintah berperilaku seperti cpatau mv, yaitu jika ia melihat "direktori" di <destination> ia akan meletakkan file di dalamnya, kalau tidak, <destination> tidak ada, itu akan membuat <destination>. (yang mana trik "slashdot" terhindar - artinya trik <destinasi> ada dan merupakan direktori).
Tapi saya bisa saja salah.

Dalam kedua kasus ini tampaknya berhasil

ln -sfv --no-dereference /tmp/test_symlink/repo/resources/snippets/ foo/snippets
the_velour_fog
sumber