cp を使用してソフトリンクをハードリンクに変換する

cp を使用してソフトリンクをハードリンクに変換する

コマンドcpのページには、次のinfoオプションが表示されます。--preserve=

links
対応するソースファイル間のリンクを宛先ファイルに保存します。-H'と組み合わせると-L' or、このオプションはできるシンボリックリンクをハードリンクに変換する

続いて、私には(今は)分からない例が続きます。とにかく:

質問: ソフトリンクをハードリンクに変換するにはどうすればいいですか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 ba: と呼ばれるソフトリンクを作成します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;: ファイルをaおよびbディレクトリにコピーします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/(最初の列は inode 番号です)。

    $ 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

info cpドキュメント内の coreutils チーム @gnu.org にバグの可能性に関するレポートを送信したところ、次のような返信が返ってきました。

このドキュメントは少々簡潔です。主な問題は、-a が -d を暗示し、それが --no-dereference を暗示することであり、これはコマンドを期待どおりに動作させるのに必要です。つまり、cp がソース内のシンボリックリンクを暗黙的にたどるのを止めるには --no-dereference が必要です。

ここで説明されている詳細を検証して分割するには、次の手順を実行します。

$ 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}、 は同じ inode 番号を持つ 2 つのファイルを認識し、認識されたハード リンクを保持します。
  • はシンボリックリンクをコピーすること@option{-a}を意味します@option{--no-dereference}が、後者はコマンドライン引数を逆参照するように@option{-H}指示し、同じ inode 番号を持つ 2 つのファイルを確認します。次に、 によっても暗示されるオプションは、認識されたハードリンクを保持します。@command{cp}@option{--preserve=links}@option{-a}

答え3

ハードリンクをシンボリックリンクに変換するのは難しいでしょう。ハードリンクの場合、ファイルシステム上に 2 つ以上のファイルエントリがポイントしているデータブロックがあります。「ソース」と「宛先」はありません。文字どおり、複数の同等の名前を持つ 1 つのファイルです。GNU find を使用して、次のように識別できます。

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

同じ inode を持つすべてのファイルを取得したら、そのうちの 1 つを「実際の」ファイルとして選択し、他のすべてのファイルをマスター ファイルへのシンボリック リンクに置き換える必要があります。おそらく、これを行うには、次の方法を使用します。

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

そして、スクリプトで同じ番号を持つ値の 1 つを選択して、他のすべての値をそれにリンクさせる方法を考えます。おそらく最初の値がターゲットになり、同じ inode を持つ他の値はそれにシンボリックリンクされます。これは、非常にシンプルでテストされていないシェル スクリプトの例です。

#!/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) が絶対パスでない場合、異なるディレクトリからのリンクが無効なターゲットで作成される可能性があるという潜在的な問題があります。ただし、基本的な考え方は問題ないはずです。

関連情報