使用 cp 將軟連結轉換為硬連結

使用 cp 將軟連結轉換為硬連結

cp命令的頁面提供以下info選項:--preserve=

links
在目標檔案中保留相應來源檔案之間的任何連結。注意-L' or-H',這個選項將符號鏈接轉換為硬鏈接

接下來是一個我現在不明白的例子;無論如何:

問題: 如何將軟連結變成硬連結cp?還有回來的方法嗎[將硬連結轉換為軟連結]?


次要問題: 哪裡有在上面的引用中發揮作用嗎?我理解-L和的目的-H,我能夠複製功能齊全的軟鏈接等,但到目前為止我還沒有設法將軟鏈接變成硬鏈接。

答案1

資訊頁面中的範例向您展示了該範例有點難以理解:

$ mkdir c; : > a; ln -s a b; cp -aH a b c; ls -i1 c
74161745 a
74161745 b

讓我們將其分解為其組件命令:

  • mkdir c;: 建立目錄c/
  • : > a;:只是建立空文件的快速方法。它相當於echo "" > a.:是一個內建的 bash,它不執行任何操作,請參閱help :
  • ln -s a b:建立一個軟連結來a呼叫b。此時,目前目錄的內容如下:

    $ ls -l | cc2ter 
    total 4
    -rw-r--r-- 1 terdon terdon    0 Oct  9 02:50 a
    lrwxrwxrwx 1 terdon terdon    1 Oct  9 02:50 b -> a
    drwxr-xr-x 2 terdon terdon 4096 Oct  9 02:50 c
    

    請注意,這b是一個符號連結(軟連結),它並不指向與以下相同的 inode a

    $ ls -i1c a b
    16647344 a
    16647362 b
    
  • cp -aH a b c;:將檔案複製ab目錄中c。這是轉換發生的地方,傳遞給的選項cp是:

    -a, --archive
          same as -dR --preserve=all
    -d    same as --no-dereference --preserve=links
    -H    follow command-line symbolic links in SOURCE
    

    -H是必要的,因為(來自info cp):

    當從符號連結複製時,「cp」通常僅在不遞歸複製時才遵循該連結。

    由於-a啟動遞歸複製 ( -R),-H因此需要遵循符號連結。-H意味著儘管遞歸,連結仍被跟踪,並將導致在目標目錄中建立硬連結。這些是c/最後一步之後的內容(第一列是索引節點號):

    $ ls -li c 
    total 0
    17044704 -rw-r--r-- 2 terdon terdon 0 Oct  9 02:50 a
    17044704 -rw-r--r-- 2 terdon terdon 0 Oct  9 02:50 b
    

現在關於它到底是如何工作的,據我通過使用它可以弄清楚,cp --preserve=links結合-L-H將符號鏈接轉換為硬鏈接,如果連結和目標都被複製到相同的目錄


事實上,作為OP發現至少在 Debian 系統上,cp --preserve=links如果目標目錄相同,就足以將符號連結轉換為硬連結。

答案2

我已在文件中向 coreutils 團隊 @gnu.org 發送了有關可能錯誤的報告,info cp並得到了回應:

這裡的文檔有點簡潔。主要問題是 -a 暗示 -d 並暗示 --no-dereference ,這是讓命令按預期工作所必需的。需要 IE --no-dereference 來隱式停止 cp 遵循來源中的符號連結。

要驗證並拆分此處演示的詳細資訊:

$ mkdir links; : > a; ln -s a b;

在這裡我們看到 -d 覆蓋了後面的 -H。因此,我們首先不會取消引用符號連結。

$ rm links/*; cp -H -d a b links
$ l links/
lrwxrwxrwx. 1 padraig 1 Oct 10 09:37 b ▪▶ a
-rw-rw-r--. 1 padraig 0 Oct 10 09:37 a

在這裡我們看到 -H 現在被尊重,因為它排在最後,因此在源中遵循符號鏈接,從而在目標中產生硬鏈接。

$ rm links/*
$ rm links/*; cp -d -H a b links
$ l links
-rw-rw-r--. 2 padraig 0 Oct 10 09:37 b
-rw-rw-r--. 2 padraig 0 Oct 10 09:37 a

我將透過以下內容使文件更加明確:

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index b273627..aeed4ca 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -8257,9 +8257,11 @@ $ mkdir c; : > a; ln -s a b; cp -aH a b c; ls -i1 c
 @noindent

請注意輸入:@file{b}是常規文件 的符號鏈接@file{a},但目標目錄 中的文件@file{c/}是硬鏈接的。

  • 由於@option{-a}暗示@option{--preserve=links},並且由於@option{-H}告訴@command{cp}取消引用命令行參數,因此它會看到兩個具有相同索引節點號的文件,並保留感知的硬連結。
  • 因為@option{-a}意味著@option{--no-dereference}它將複製符號鏈接,但後者@option{-H}告訴@command{cp}取消引用命令行參數,然後它會看到兩個具有相同索引節點號的檔案。然後,該@option{--preserve=links} 選項也暗示@option{-a}將保留感知的硬連結。

答案3

將硬連結轉換為符號連結是很困難的。在硬連結的情況下,檔案系統上有一個資料區塊,有兩個或多個檔案條目指向它。沒有“來源”和“目的地”;它實際上是一個具有多個等效名稱的檔案。您可以使用 GNU find 來識別這些:

sauer@zipper:~$ find . -type f -links +1 -printf "%i: %p (%n)\n"
609: ./link1 (2)
609: ./link2 (2)

一旦你獲得了具有相同 inode 的所有文件,你就必須選擇一個作為「真實」文件,然後用主文件的符號連結替換所有其他文件。可能的方法是使用這個:

sauer@zipper:~$ find . -type f -links +1 -printf "%i %p\n" | sort -nk1
609 ./link1
609 ./link2

然後讓一個腳本弄清楚如何選擇具有相同數字的值之一以使所有其他值連結到它。也許第一個成為目標,並且具有相同 inode 的任何其他都被符號連結到它。這是一個非常簡單、未經測試的 shell 腳本範例

#!/bin/sh
prev=""
target=""
find /tmp -type f -links +1 -printf "%i %p\n" | sort -nk1 \
| while read inode file
do
  if [[ $inode != $prev ]]
  then
     target="$file"
     prev=$inode
  else
    ln -sf "$target" "$file"
  fi
done

存在潛在的問題,因為如果 find 中的路徑(本例中為 /tmp)不是絕對路徑,則可能會使用無效目標建立來自不同目錄的連結。但整體思路應該沒問題。

相關內容