간단한 Linux 명령으로 절대 symlink를 상대 symlink로 변환


29

나는 경로 내부의 완전한 하위 파일 시스템이 /home/user/system디렉토리와 표준 리눅스 구조를 포함을 /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 명의 응답 된 사용자가 있습니다 : stackoverflow.com/questions/2564634/… . 해당 스레드에는 몇 가지 잠재적 인 솔루션이 있지만 각각은 처리해야 할 특정 상황과 그렇지 않은 다른 상황이 있습니다.
slm

답변:


20

Mark Lord 의 symlinks유틸리티 를 사용하여 (많은 배포판이 제공합니다.없는 경우 소스 에서 빌드하십시오 ) :

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, $2for link; do …(와 동의어이다 대해 반복 for link in "$@"; do …). 에서 오는 오류 find는 오타로 인한 것입니다 (에 대한 인수 주위에 작은 따옴표 대신 역 따옴표를 입력했습니다 -lname).
Gilles 'SO- 악의를 멈추십시오'8:13에

이제 스크립트가 실행되지만 예상대로 실행되지 않습니다. 어쩌면 내가 정확하지 않았을 수도 있습니다. 질문을 업데이트했습니다.
Alex

@Alex 문제가 무엇입니까? 나는 원칙이 건전하다고 생각하지만 코딩 실수를 저지른 것 같습니다. 시도 했습니까 symlinks? 그것은 당신의 문제를 해결할 것입니다-그것은 기본적으로 그것이 설계된 것입니다 (당신이 그것을 근절 해야한다는 성가심으로 ( fakechroot 가 트릭을 수행해야 함)).
Gilles 'SO- 악마 그만해

1
추가로 설치할 필요없이 즉시 사용 가능한 솔루션을 강력하게 제공합니다.
Alex

4

순수 bash 및 coreutils ../는 경로에 불필요한 s 없이 symlink를 상대로 변경 합니다.

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

이 배쉬 스 니펫이해야합니다. 첫 번째 양식은 수행 할 작업을 인쇄합니다. 두 번째는 그것을 할 것입니다. 나는 출력이 파괴적이므로 신중하게 검토 할 것 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 / 홈 / 사용자 / 시스템 및
찾기 -lname '/ *'|
읽는 동안 l; 해야 할 것
  echo ln -sf $ (echo $ (echo $ l | sed 's | / [^ /] * | / .. | g') $ (readlink $ l) | sed 's /.....//' ) $ l
완료 |
쉬

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.