동일한 디렉토리를 가리키는 심볼릭 링크를 계속 따라갈 때 어떤 일이 발생하는지 확인하기 위해 작은 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
여기서 무슨 일이 일어나고 있는 걸까요?
답변1
Patrice는 문제의 원인을 식별합니다.그의 대답, 그러나 거기에서 어떻게 도달하는지, 왜 이것을 얻는지 궁금하다면 이야기가 길어집니다.
프로세스의 현재 작업 디렉터리는 복잡하지 않습니다. 이는 프로세스의 속성이며 상대 경로(프로세스가 수행한 시스템 호출)에서 시작하는 디렉터리 유형 파일에 대한 핸들입니다. 상대 경로를 확인할 때 커널은 현재 디렉터리의 전체 경로를 알 필요가 없으며 단순히 해당 디렉터리의 파일에서 디렉터리 항목을 읽어 상대 경로의 첫 번째 구성 요소( ..
다른 파일과 마찬가지로)를 찾은 다음 거기 계속.
이제 사용자는 디렉터리 트리에서 이 디렉터리가 어디에 있는지 알고 싶을 때가 있습니다. 대부분의 Unices의 경우 디렉터리 트리는 루프가 없는 단일 트리입니다. 즉, /
트리의 루트( )에서 특정 파일까지의 경로는 단 하나뿐입니다. 이 경로를 흔히 표준 경로라고 합니다.
현재 작업 디렉토리에 대한 경로를 얻으려면 프로세스가 해야 할 일은 위로 올라가는 것뿐입니다.아래에뿌리가 아래쪽에 있는 나무를 보고 싶다면) 나무를 뿌리로 되돌리고 도중에 있는 노드의 이름을 찾으세요.
예를 들어, 현재 디렉토리가 무엇인지 알아내려는 프로세스는 해당 디렉토리(현재 디렉토리의 항목과 같은 상대 경로 )를 /a/b/c
열고 동일한 inode 번호를 가진 디렉토리 유형 파일을 찾고 일치하는 항목을 찾은 다음 그것을 열어 보세요. 그리고 당신이 그것을 찾을 때까지 계속됩니다 . 거기에는 모호함이 없습니다...
..
.
c
../..
/
이것이 C 함수가 하는 getwd()
일 getcwd()
, 또는 적어도 예전에는 했던 일입니다.
일부 시스템(예: 최신 Linux)에는 커널 공간에서 조회되는 현재 디렉터리의 정식 경로를 반환하는 시스템 호출이 있습니다. 모든 항목에 대한 읽기 액세스 권한이 없더라도 현재 디렉터리를 찾을 수 있습니다. 그 구성 요소) , 이것을 이라고 합니다 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는 결국 이 동작을 지정했습니다. 즉, 이제 대부분의 쉘도 이 동작을 수행합니다.
및 내장 명령 cd
(pwd
그리고 그들만을 위해서popd
(이는 / 가 있는 쉘 에도 적용되지만 pushd
)) 쉘은 현재 작업 디렉토리에 대한 자체 아이디어를 유지합니다. $PWD
특수 변수 에 저장됩니다 .
이 작업을 수행할 때:
cd c/d
c
or 가c/d
심볼릭 링크라도 $PWD
포함 하면 끝에 /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
다음에 호출할 때 많은 일이 발생할 수 있습니다 . 현재 디렉터리는 절대 변경되지 않지만(삭제 가능하더라도 항상 동일한 인덱스 노드임) 디렉터리 트리의 해당 경로는 완전히 변경될 수 있습니다. 현재 디렉토리는 호출할 때마다 디렉토리 트리를 탐색하여 계산되므로 해당 정보는 항상 정확하지만 POSIX 쉘로 구현된 논리 디렉토리의 경우 정보가 최신이 아닐 수 있습니다. 따라서 일부 쉘 에서는 또는 .cd
pwd
$PWD
getcwd()
$PWD
cd
pwd
이 특정 인스턴스에서는 다른 셸에서 다른 동작을 볼 수 있습니다.
어떤 사람들은 ksh93
문제를 완전히 무시하기를 좋아하므로 문제를 호출한 후에도 잘못된 정보가 반환됩니다 cd
(그리고 거기서 나타나는 동작을 볼 수 없습니다 bash
).
어떤 사람들은 그것이 여전히 현재 디렉토리에 대한 경로 인지 확인하는 것을 좋아 bash
하거나 zsh
확인 하지만 그렇지 않습니다 .$PWD
cd
pwd
pwd
pdksh는 합계를 확인합니다 cd
(그러나 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)
다양한 껍질에.
bash
귀하의 경우에는 a 이후에 사용하고 있으므로 여전히 현재 디렉토리 cd a
를 가리키는지 bash
확인하십시오 . $PWD
이를 위해 stat()
의 값을 호출하여 $PWD
inode 번호를 확인하고 이를 의 값과 비교합니다 .
.
그러나 $PWD
조회 경로에 너무 많은 기호 링크가 포함되어 있으면 오류가 반환되므로 stat()
쉘은 $PWD
현재 디렉토리에 해당하는지 확인할 수 없으므로 이를 다시 계산 getcwd()
하고 그에 따라 업데이트합니다 $PWD
.
이제 Patrice의 대답을 명확히 하기 위해 경로를 찾을 때 발견되는 심볼릭 링크 수를 확인하는 것은 심볼릭 링크 주기를 방지하는 것입니다. 가장 간단한 루프를 사용할 수 있습니다
rm -f a b
ln -s a b
ln -s b a
이러한 보호 장치가 없으면 Windows에서 cd a/x
시스템은 a
연결된 위치를 찾고 b
연결된 심볼릭 링크를 찾아야 하며 a
이는 무한정 계속됩니다. 이를 방지하는 가장 쉬운 방법은 여러 개의 심볼릭 링크를 해결한 후 포기하는 것입니다.
이제 다시논리적 현재 작업 디렉터리그리고 왜 그것은 그다지 좋은 기능이 아닌가. cd
다른 명령이 아닌 셸에서만 작동한다는 점을 인식하는 것이 중요합니다 .
예를 들어:
cd -- "$dir" && vi -- "$file"
항상 다음과 같지는 않습니다.
vi -- "$dir/$file"
cd -P
이것이 바로 혼란을 피하기 위해 사람들이 항상 스크립트에서 사용하도록 권장하는 이유입니다( 단지 다른 언어가 아닌 쉘에 작성되었다는 이유로 소프트웨어가 다른 명령과 다르게 인수를 처리하는 것을 원하지 않습니다 ).../x
이 -P
옵션은 비활성화되어 있습니다논리적 디렉터리이렇게 처리하면 cd -P -- "$var"
실제로 chdir()
콘텐츠가 호출됩니다(적어도 콘텐츠가 설정되지 않은 $var
한 , 다음 경우 를 제외하고 (또는 일부 셸에서는 ...일 수도 있지만 ) 이는 또 다른 이야기입니다). 이후에는 정식 경로가 포함됩니다.$CDPATH
$var
-
-2
+3
cd -P
$PWD
답변2
이는 서비스 거부를 방지하기 위해 Linux 커널 소스 코드에 하드 코딩된 제한으로 인해 발생합니다. 중첩된 심볼릭 링크의 수는 40개로 제한됩니다(다음에서 사용 가능).follow_link()
기능inside fs/namei.c
, nested_symlink()
커널 소스 코드에 의해 호출됨).
심볼릭 링크를 지원하는 다른 커널의 경우 유사한 동작이 나타날 수 있습니다(40 이외의 제한도 있을 수 있음).