単純なLinuxコマンドで絶対シンボリックリンクを相対シンボリックリンクに変換する


29

私は、パス内の完全なサブファイルシステムを持っている/home/user/systemディレクトリと標準のLinux構造を含みます/bin/home/root/usr/var/etc、...

このサブファイルシステムには、相対または絶対のいずれかのシンボリックリンクが含まれています。相対シンボリックリンクは問題なく、サブファイルシステム内にあります/home/user/system。しかし、絶対シンボリックリンクは、サブファイルシステムの外部のターゲットを指しているため、問題があります。

例として、次のような絶対シンボリックリンクを想定しています(サブファイルシステム内で見られます):

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

全体的なファイルシステムでは、我々は、リンクしてい/home/user/system/usr/file1たファイルへの今ポイント/usr/lib/file1の代わりに、ファイルのサブファイルシステムの外側、/home/user/system/usr/lib/file1 内側のサブファイルシステムを。

単純なスクリプト、できればすべての絶対シンボリックリンクを相対シンボリックリンクに変換する単一のコマンドライン(rsync、chroot、find、...)が欲しいです。

与えられた例では、その相対リンクは次のようになります

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

4
このQを解決しようとするに興味がある方のために、ここではSOの試みから250K rep'dユーザーのstackoverflow.com/questions/2564634/...。そのスレッドにはいくつかの潜在的な解決策がありますが、それぞれに対処する特定の状況と、対処しない他の状況があります。
slm

回答:


20

Mark Lord のsymlinksユーティリティを使用して(多くのディストリビューションで提供されています。お持ちでない場合は、sourceからビルドしてください):

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 …ループオーバーします(これはの同義語ですfor link in "$@"; do …)。からのエラーfindはタイプミスによるものです(私はどうにかしてを引数の周りに一重引用符ではなく逆引用符を入力することができました-lname)。
ジル「SO-悪であるのをやめる」

これでスクリプトは実行されますが、期待どおりには動作しません。たぶん私は十分に正確ではなかったので、質問を更新しました。
アレックス

@Alex問題は何ですか?原則は健全だと思いますが、コーディングの間違いを犯した可能性があります。試しましたsymlinksか?それはあなたの問題を解決します-それは基本的にそれが設計されたものです(あなたがそれをchrootで実行しなければならないという迷惑があります(fakechrootはトリックを行うべきです))
ジル「SO-悪であるのをやめる」

1
追加の何かをインストールする必要なく、箱から出して動作するソリューションを強く好みます。
アレックス

4

純粋なbashとcoreutils、../パスに不要なs なしでシンボリックリンクを相対に変更します:

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そのディレクトリにシンボリックリンクを変換します
  • ln -fsecho ln -fs予行演習のために

説明:

  • target="$(realpath "$l")" -symlinkターゲットへの絶対パスを見つけます
  • ln -fs-シンボリックリンク(-s)を作成し-f、既存の
  • realpath -s "$l" -シンボリックリンク自体への絶対パスを見つける
  • dirname "$(realpath -s "$l")" -シンボリックリンクを含むディレクトリへの絶対パスを見つける
  • realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target" -シンボリックリンクに関連するターゲットのパスを検索します。つまり、絶対パスを相対パスに変換します

1
if壊れたリンクをスキップする句を追加することをお勧めします。
fuenfundachtzig

0

このbashスニペットはそれを行う必要があります。最初のフォームは、それがすることを印刷します。2番目はそれを行います。出力は破壊的であるため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

これが純粋なshソリューションです。

cd / home / user / system &&
見つける。-lname '/ *' |
lを読みながら 行う
  echo ln -sf $(echo $(echo $ l | sed 's | / [^ /] * | / .. | g')$(readlink $ l)| sed 's /.....//' )$ l
完了|
sh

0

相対リンクの絶対リンクの変換は、sshを介してリモートディレクトリをマウントするsshfsでサポートされています。

コマンドは次のとおりです。

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.