심볼릭 링크 재귀- "재설정"하는 이유는 무엇입니까?


64

나는 같은 디렉토리를 가리키는 심볼릭 링크를 계속 유지할 때 어떤 일이 발생하는지 확인하기 위해 작은 bash 스크립트를 작성했습니다. 매우 긴 작업 디렉토리를 만들거나 충돌 할 것으로 예상했습니다. 그러나 결과는 나를 놀라게했다 ...

mkdir a
cd a

ln -s ./. a

for i in `seq 1 1000`
do
  cd a
  pwd
done

출력 중 일부는

${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a
${HOME}/a/a
${HOME}/a/a/a
${HOME}/a/a/a/a
${HOME}/a/a/a/a/a
${HOME}/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a

여기서 무슨 일이 일어나고 있습니까?

답변:


88

파트리스는 그의 대답 에서 문제의 원인을 확인 했지만 거기에서 어떻게 얻는 지 알고 싶다면 여기에 긴 이야기가 있습니다.

프로세스의 현재 작업 디렉토리는 너무 복잡하다고 생각하지 않습니다. 프로세스의 시스템 속성에서 프로세스가 호출 한 상대 경로가 시작되는 디렉토리 유형의 파일에 대한 핸들 인 프로세스의 속성입니다. 상대 경로를 해결할 때 커널은 (a) 현재 디렉토리의 전체 경로를 알 필요가 없으며 상대 경로의 첫 번째 구성 요소를 찾기 위해 해당 디렉토리 파일의 디렉토리 항목을 읽습니다 ( ..다른 경로와 유사 함) 그 점에서 파일)을 계속하고 거기에서 계속됩니다.

이제는 사용자로서 때때로 디렉토리 트리에서 디렉토리가 어디에 있는지 알고 싶어합니다. 대부분의 Unices에서 디렉토리 트리는 루프가없는 트리입니다. 즉, 트리의 루트 ( /)에서 지정된 파일 까지의 경로는 하나뿐입니다 . 이 경로는 일반적으로 정식 경로라고합니다.

프로세스가 그냥 도보 무슨 상관이 현재 작업 디렉토리의 경로를 얻으려면 (물론 아래 는 바닥에 뿌리를 가진 나무를보고 싶은 경우) 노드의 이름을 찾는 루트에 다시 나무를 도중에.

예를 들어, 현재 디렉토리가이라는 것을 찾으려고하는 프로세스 /a/b/c..디렉토리를 열고 (상대 경로도 ..현재 디렉토리의 항목) 디렉토리와 같은 inode 번호를 가진 디렉토리 유형의 파일을 .찾습니다. c를 찾은 다음 ../..찾을 때까지 계속 열립니다 /. 거기에 모호성이 없습니다.

즉, 무엇 getwd()이나 getcwd()C의 기능을 수행하거나 적어도 수행하는 데 사용됩니다.

현대 리눅스와 같은 일부 시스템에는 커널 공간에서 조회를 수행하는 현재 디렉토리로의 표준 경로를 반환하는 시스템 호출이 있습니다 (모든 구성 요소에 대한 읽기 권한이 없어도 현재 디렉토리를 찾을 수 있습니다) 그것이 바로 getcwd()전화입니다. 최신 Linux에서는 readlink ()를 통해 현재 디렉토리의 경로를 찾을 수도 있습니다 /proc/self/cwd.

이것이 현재 디렉토리의 경로를 반환 할 때 대부분의 언어와 초기 쉘이하는 일입니다.

귀하의 경우에는, 당신은 호출 할 수 있습니다 cd a당신이 원하는대로에 심볼릭 링크 때문에, 수도 등의 시간을 .현재 디렉토리 그래서 모든 변경되지 않습니다, getcwd(), pwd -P, python -c 'import os; print os.getcwd()', perl -MPOSIX -le 'print getcwd'당신의 반환합니다 ${HOME}.

이제 심볼릭 링크가 모든 것을 복잡하게 만들었습니다.

symlinks디렉토리 트리에서 점프를 허용합니다. 에서 /a/b/c, 경우 /a/a/b또는 /a/b/c의 다음 정식 경로 심볼릭 링크이며, /a/b/c완전히 다른 무언가를 할 것이다. 특히의 ..항목 /a/b/c이 반드시 그런 것은 아닙니다 /a/b.

Bourne 쉘에서 다음을 수행하십시오.

cd /a/b/c
cd ..

또는:

cd /a/b/c/..

에 끝날 것이라는 보장은 없습니다 /a/b.

처럼:

vi /a/b/c/../d

반드시 다음과 같을 필요는 없습니다.

vi /a/b/d

ksh논리적 인 현재 작업 디렉토리 개념을 도입하여 어떻게 든 해결할 수 있습니다. 사람들은 그것에 익숙해졌고 POSIX는 그 동작을 지정하여 결국 요즘 대부분의 쉘에서도 그렇게합니다.

의 경우 cdpwd(내장 명령 만 그들을 위해 (도에 대해는 비록 popd/ pushd을)이 포탄), 쉘은 현재 작업 디렉토리의 자신의 아이디어를 유지한다. $PWD특수 변수에 저장됩니다 .

할 때 :

cd c/d

하더라도 c또는 c/d동시에 심볼릭이다 $PWD는 containes은 /a/b, 그것이 추가 c/d단부에 그렇게 $PWD된다 /a/b/c/d. 그리고 당신이 할 때 :

cd ../e

일을 대신에 chdir("../e"), 그렇습니다 chdir("/a/b/c/e").

그리고 pwd명령은 $PWD변수 의 내용 만 반환합니다 .

때문 대화 형 쉘에서 유용 pwd당신이 거기에 도착하는 방법에 대한 정보를 제공하고 한 경우에만 사용으로 현재 디렉토리 경로 출력 ..에 대한 인수 cd및 기타되지 명령, 그것은 당신을 놀라게 할 가능성이 낮아을, 때문에 cd a; cd ..또는 cd a/..것은 일반적으로 당신에게 돌아 당신이 있던 곳으로.

이제는 $PWD하지 않으면 수정되지 않습니다 cd. 다음에 cd또는을 ( 를) 호출 할 때까지 pwd많은 일이 발생할 $PWD수 있으며 구성 요소 이름을 바꿀 수 있습니다. 현재 디렉토리는 절대 변경되지 않지만 (삭제 될 수는 있지만 항상 동일한 inode 임) 디렉토리 트리의 경로는 완전히 변경 될 수 있습니다. getcwd()디렉토리 트리를 내려 가면서 호출 될 때마다 현재 디렉토리를 계산하여 정보가 항상 정확하지만 POSIX 쉘로 구현 된 논리 디렉토리의 경우 정보 $PWD가 오래 될 수 있습니다. 그래서 실행에 cd또는 pwd일부 포탄은 방지 할 수 있습니다.

특정 인스턴스에서 다른 쉘을 가진 다른 동작을 볼 수 있습니다.

일부 등 ksh93문제를 완전히 무시하고, 그래서 당신은 전화 후에도 잘못된 정보를 반환합니다 cd(당신은 당신이보고있는 동작 볼 것 bash거기를).

어떤 사람들은 그것이 현재 디렉토리에 대한 경로인지 확인 bash하거나 zsh확인 하지는 않습니다 .$PWDcdpwd

pdksh 같은 두에 확인 않습니다 pwdcd(하지만,시 pwd, 업데이트하지 않습니다 $PWD)

ash(적어도 데비안에서 찾은 것)은 확인하지 않으며 , 그렇게 할 cd a때 실제로 cd "$PWD/a"하므로 현재 디렉토리가 변경되어 $PWD더 이상 현재 디렉토리를 가리 키지 않으면 실제로 a는 현재 디렉토리 의 디렉토리로 변경되지 않습니다 이지만 그 중 하나에 $PWD오류가 있으면 오류를 반환합니다.

당신이 그것을 가지고 놀고 싶다면, 당신은 할 수 있습니다 :

cd
mkdir -p a/b
cd a
pwd
mv ~/a ~/b 
pwd
echo "$PWD"
cd b
pwd; echo "$PWD"; pwd -P # (and notice the bug in ksh93)

다양한 껍질에.

귀하의 경우에는, 당신은 사용하고 있기 때문에 bashcd a, bash확인 $PWD여전히 현재 디렉토리를 가리 킵니다. 그렇게하기 위해서는 호출 stat()의 값에 $PWD그 아이 노드 번호를 확인하고 그와 비교하는 ..

그러나 $PWD경로를 찾는 데 너무 많은 심볼릭 링크를 해결 stat()하면 오류와 함께 반환되므로 쉘은 $PWD여전히 현재 디렉토리에 해당 하는지 여부를 확인할 수 없으므로 다시 계산하여 getcwd()업데이트합니다 $PWD.

이제 Patrice의 답변을 명확히하기 위해 경로를 찾는 동안 발생하는 심볼릭 링크 수를 확인하는 것은 심볼릭 링크 루프를 방지하는 것입니다. 가장 간단한 루프는

rm -f a b
ln -s a b
ln -s b a

안전 장치가 없으면 cd a/x시스템은에 대한 a링크를 찾아야하며 링크를 찾고 링크를 b하는 심볼릭 링크이며 a, 무한정 계속됩니다. 이를 막는 가장 간단한 방법은 임의의 수의 심볼릭 링크를 초과하여 해결 한 후 포기하는 것입니다.

이제 논리적 인 현재 작업 디렉토리로 돌아가서 왜 그렇게 좋지 않은 기능이 되었습니까? cd다른 명령이 아닌 셸 에서만 사용된다는 것을 인식하는 것이 중요 합니다.

예를 들어 :

cd -- "$dir" &&  vi -- "$file"

항상 다음과 같은 것은 아닙니다.

vi -- "$dir/$file"

그렇기 때문에 사람들이 cd -P혼란을 피하기 위해 항상 스크립트에서 사용하는 것이 좋습니다 ( ../x다른 언어 대신 쉘로 작성 되었기 때문에 소프트웨어가 다른 명령 과 다르게 인수를 처리하기를 원하지 않는 경우가 있습니다).

-P옵션은 비활성화하는 것입니다 논리적 디렉토리 그렇게 처리 cd -P -- "$var"호출 않는 사실 chdir()의 내용에 $var(때를 제외을 $var하다 -그 다른 이야기에 불과). 그리고 후 cd -P, $PWD정식 경로가 포함됩니다.


7
사랑스런 예수님! 이러한 포괄적 인 답변에 감사드립니다. 정말 흥미 롭습니다 :)
Lucas

굉장한 대답, 고마워요! 나는 기분이 이러한 모든 것들을 알고,하지만 난 이해하거나 그들이 모두 함께 온 방법에 대해 생각 없었어요. 좋은 설명입니다.
dimo414

42

이것은 Linux 커널 소스에서 하드 코딩 된 한계의 결과입니다. 서비스 거부를 방지하기 위해 중첩 된 심볼릭 링크 수의 제한은 40입니다 ( 커널 소스에서 호출하는 inside follow_link()함수에 있음 ).fs/namei.cnested_symlink()

심볼릭 링크를 지원하는 다른 커널과 비슷한 동작 (그리고 아마도 40이 아닌 다른 한계)을 얻을 수 있습니다.


1
멈추지 않고 "재설정"해야 할 이유가 있습니까? 즉 x%40오히려 max(x,40). 여전히 디렉토리가 변경되었음을 알 수 있습니다.
Lucas

4
다른 사람이 궁금해하는 소스 링크 : lxr.linux.no/linux+v3.9.6/fs/namei.c#L818
Ben
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.