ls의 실제 하드 링크를 어떻게 볼 수 있습니까?


97

난 달린다

ln /a/A /b/B

a파일 A가 가리키는 폴더를보고 싶습니다 ls.


1
하드 링크는 포인터가 아니며 심볼릭 링크입니다. 그들은 같은 파일 (inode)에 대한 여러 이름입니다. link(2)시스템 호출 후 어떤 것이 원래인지, 어떤 것이 링크인지는 의미가 없습니다. 그렇기 때문에 답변에서 알 수 있듯이 모든 링크를 찾는 유일한 방법은 find / -samefile /a/A입니다. inode에 대한 하나의 디렉토리 항목은 동일한 inode에 대한 다른 디렉토리 항목을 "알지"않기 때문입니다. 그들이하는 모든 것은 inode를 다시 계산하기 때문에 그 이름이이면 삭제 될 수 있습니다 unlink(2)ed. ( ls출력 의 "링크 수"입니다 .)
Peter Cordes

@PeterCordes : 참조 카운트가 실제로 하드 링크 항목에 저장되어 있습니까? 그것이 당신의 말이 암시하는 것입니다 ( "그들이하는 모든 것은 아이 노드를 재검토하는 것입니다 ...") 그러나 링크가 서로에 대해 아무것도 모르는 경우에는 이해가되지 않을 것입니다. 업데이트됩니다. 아니면 심판이 아이 노드 자체에 저장되어 있습니까? (멍청한 질문이라면 저를 용서하십시오. 나는 초보자라고 생각하며 여전히 배우고 있습니다).
loneboat

1
refcount는 결국 다른 사실에서 알 수 있듯이 inode에 저장됩니다. :) 디렉토리 항목의 이름은 inode에 대한 포인터입니다. 동일한 inode를 가리키는 여러 이름이있는 경우이를 "하드 링크"라고합니다.
Peter Cordes

답변:


171

파일의 inode 번호를 찾을 수 있습니다

ls -i

ls -l

참조 횟수를 표시합니다 (특정 inode에 대한 하드 링크 수)

inode 번호를 찾은 후 동일한 inode를 가진 모든 파일을 검색 할 수 있습니다.

find . -inum NUM

현재 디렉토리 (.)에 inode NUM의 파일 이름을 표시합니다.


46
find를 실행할 수 있습니다. -samefile 파일 이름
BeowulfNode42

1
@ BeowulfNode42이 명령은 훌륭하지만 최소한 동일한 파일의 공유 루트 폴더가 필요합니다.
Itachi

1
이 답변은 실용적인 "할 일"을 제공하지만 @LaurenceGonsalves 가 "어떻게"및 / 또는 "왜"질문에 대답 한다고 강력하게 느낍니다 .
Trevor Boyd Smith

65

귀하의 질문에 대한 명확한 대답이 없습니다. 심볼릭 링크와 달리 하드 링크는 "원본 파일"과 구별 할 수 없습니다.

디렉토리 항목은 파일 이름과 inode에 대한 포인터로 구성됩니다. inode에는 파일 메타 데이터와 실제 파일 내용을 가리키는 포인터가 포함됩니다. 하드 링크를 만들면 동일한 inode에 대한 다른 파일 이름 + 참조가 생성됩니다. 이러한 참조는 단방향 (일반적인 파일 시스템에서는)이며 inode는 참조 카운트 만 유지합니다. "original"파일 이름을 찾는 고유 한 방법은 없습니다.

그건 그렇고, 파일을 "삭제"하는 시스템 호출이 호출되는 이유 unlink입니다. 하드 링크 만 제거합니다. 첨부 된 데이터는 inode의 참조 카운트가 0으로 떨어지는 경우에만 삭제됩니다.

주어진 inode에 대한 다른 참조를 찾는 유일한 방법은 문제의 inode를 참조하는 파일을 검사하는 파일 시스템을 철저히 검색하는 것입니다. 쉘에서 'test A -ef B'를 사용하여이 점검을 수행 할 수 있습니다.


35
즉 , 원본 파일도 하드 링크이므로 다른 파일에 대한 하드 링크와 같은 것은 없습니다 . 하드 링크 는 디스크상의 위치를 가리 킵니다 .
jtbandes

12
@jtbandes : 하드 링크는 실제 데이터를 가리키는 inode를 가리 킵니다.
dash17291

33

UNIX는 하드 링크와 심볼릭 링크 (로 만든이 "ln""ln -s"각각을). 심볼릭 링크는 단순히 다른 파일에 대한 실제 경로를 포함하고 파일 시스템을 통과 할 수있는 파일입니다.

하드 링크는 유닉스 초기 초기부터 사용되어 왔습니다 (어쨌든 기억할 수 있으며 꽤 오래 전입니다). 이들은 정확히 동일한 기본 데이터 를 참조하는 두 개의 디렉토리 항목입니다 . 파일의 데이터는로 지정됩니다 inode. 파일 시스템의 각 파일은 inode를 가리 키지 만 각 파일이 고유 한 inode를 가리킬 필요는 없습니다. 즉, 하드 링크가 시작됩니다.

inode는 주어진 파일 시스템에서만 고유하기 때문에 하드 링크가 동일한 파일 시스템에 있어야한다는 제한이 있습니다 (심볼릭 링크와 달리). 심볼릭 링크와 달리 권한있는 파일은 없습니다. 모두 동일합니다. 데이터 영역은 해당 inode를 사용하는 모든 파일이 삭제 된 경우에만 해제 됩니다 (그리고 모든 프로세스가 데이터를 닫지 만 다른 문제입니다).

"ls -i"명령을 사용하여 특정 파일의 inode를 가져올 수 있습니다 . 그런 다음 "find <filesystemroot> -inum <inode>"명령을 사용하여 주어진 inode가있는 파일 시스템의 모든 파일을 찾을 수 있습니다 .

정확하게 수행하는 스크립트는 다음과 같습니다. 다음과 같이 호출하십시오.

findhardlinks ~/jquery.js

그리고 해당 파일 시스템에서 해당 파일에 대한 하드 링크 인 모든 파일을 찾습니다.

pax@daemonspawn:~# ./findhardlinks /home/pax/jquery.js
Processing '/home/pax/jquery.js'
   '/home/pax/jquery.js' has inode 5211995 on mount point '/'
       /home/common/jquery-1.2.6.min.js
       /home/pax/jquery.js

여기 스크립트가 있습니다.

#!/bin/bash
if [[ $# -lt 1 ]] ; then
    echo "Usage: findhardlinks <fileOrDirToFindFor> ..."
    exit 1
fi

while [[ $# -ge 1 ]] ; do
    echo "Processing '$1'"
    if [[ ! -r "$1" ]] ; then
        echo "   '$1' is not accessible"
    else
        numlinks=$(ls -ld "$1" | awk '{print $2}')
        inode=$(ls -id "$1" | awk '{print $1}' | head -1l)
        device=$(df "$1" | tail -1l | awk '{print $6}')
        echo "   '$1' has inode ${inode} on mount point '${device}'"
        find ${device} -inum ${inode} 2>/dev/null | sed 's/^/        /'
    fi
    shift
done

@pax : 스크립트에 버그가있는 것 같습니다. . ./findhardlinks.bashOS X의 Zsh에있는 동안 시작합니다 . 화면의 현재 창이 닫힙니다.

4
@Masi 문제는 처음입니다. (소스 명령과 동일). 그러면 exit 1 명령이 쉘을 종료합니다. chmod a + x findhardlinks.bash를 사용한 다음 ./findhardlinks.bash로 실행하거나 bash findhardlinks.bash
njsf를 사용하십시오.


3
프로그래밍 방식 으로이 작업을 수행하려면 대신 이것을 사용하면 더 탄력적 INUM=$(stat -c %i $1)입니다. 또한 NUM_LINKS=$(stat -c %h $1). man stat사용할 수있는 더 많은 형식 변수를 참조하십시오 .
Joe

최고의 답변. 명성.
MariusMatutiae

24
ls -l

첫 번째 열은 권한을 나타냅니다. 두 번째 열은 하위 항목의 수 (디렉토리의 경우) 또는 동일한 데이터에 대한 경로 (원본 파일을 포함한 하드 링크)의 수입니다. 예 :

-rw-r--r--@    2    [username]    [group]    [timestamp]     HardLink
-rw-r--r--@    2    [username]    [group]    [timestamp]     Original
               ^ Number of hard links to the data

2
주어진 파일에 [other] 하드 링크가 있는지 확인하는데 도움이되지만 어디에 있지는 않습니다.
mklement0

또한 하드 링크와 원본 파일 사이에는 기술적 인 차이가 없습니다. 그것들은 단순히 inode디스크 내용을 가리킨다 는 점에서 동일하다 .
guyarad

13

다음과 같은 간단한 것은 어떻습니까? 나중에 위의 긴 스크립트를 대체 할 수 있습니다!

특정 파일이 <THEFILENAME>있고 모든 하드 링크가 디렉토리에 퍼져있는 것을 알고 싶다면 <TARGETDIR>(전체 파일 시스템으로 표시 될 수도 있음 /)

find <TARGETDIR> -type f -samefile  <THEFILENAME>

<SOURCEDIR>여러 개의 하드 링크가 퍼져 있는 모든 파일을 알고 싶다면 논리를 확장하십시오 <TARGETDIR>.

find <SOURCEDIR> -type f -links +1   \
  -printf "\n\n %n HardLinks of file : %H/%f  \n"   \
  -exec find <TARGETDIR> -type f -samefile {} \; 

이것은 나에게 가장 좋은 대답입니다! 그러나 -type f파일도 디렉토리 일 수 있기 때문에 사용하지 않을 것 입니다.
silvio

3
@silvio : 디렉토리가 아닌 파일에 대한 하드 링크 만 만들 수 있습니다 .
mklement0

@ mklement0 : 당신이 맞아요!
silvio

디렉토리 의 ...항목은 하드 링크입니다. 의 링크 수에서 디렉토리에 몇 개의 하위 디렉토리가 있는지 알 수 있습니다 .. 어쨌든 출력물을 find -samefile .인쇄하지 않기 때문에 어리석은 짓입니다 subdir/... find(최소한 GNU 버전)은 ..조차도 무시하도록 하드 코딩 된 것 같습니다 -noleaf.
Peter Cordes

또한, 모든 링크 찾기 아이디어는 하드 링크 된 파일 세트의 각 멤버마다 한 번씩 O(n^2)실행됩니다 find. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate(16은 2 ^ 63-1의 10 진수 표현을 위해 충분히 넓지 않기 때문에 XFS 파일 시스템이 inode 수를 크게하기에 충분히 크면 조심하십시오)
Peter Cordes

5

파일 시스템에서 모든 하드 링크를 찾기위한 스크립트에는 많은 답변이 있습니다. 대부분의 경우 find를 실행하여 전체 파일 시스템을 스캔하여 -samefile각 다중 링크 파일을 찾습니다 . 이것은 미쳤다. inode 번호를 정렬하고 사본을 인쇄하기 만하면됩니다.

파일 시스템을 한 번만 통과하면 모든 하드 링크 된 파일 세트를 찾아 그룹화 할 수 있습니다

find dirs   -xdev \! -type d -links +1 -printf '%20D %20i %p\n' |
    sort -n | uniq -w 42 --all-repeated=separate

이것은 여러 세트의 하드 링크 된 파일을 찾는 다른 답변보다 훨씬 빠릅니다 .
find /foo -samefile /bar하나의 파일에 적합합니다.

  • -xdev: 하나의 파일 시스템으로 제한합니다. FS-id를 uniq로 인쇄하기 때문에 꼭 필요한 것은 아닙니다.
  • ! -type d거부 디렉토리 : ...항목은 항상 연결되어 있음을 의미합니다.
  • -links +1 : 링크 수를 엄격하게 > 1
  • -printf ...FS-id, inode 번호 및 경로를 인쇄하십시오. (우리가 알 수있는 고정 열 너비에 패딩이 있음 uniq)
  • sort -n | uniq ... 빈 줄로 그룹을 구분하여 처음 42 개의 열에서 숫자 정렬 및 고유 화

를 사용하면 ! -type d -links +1sort의 입력이 uniq의 최종 출력만큼 ​​크므로 많은 양의 문자열 정렬을 수행하지 않습니다. 하드 링크 세트 중 하나만 포함 된 서브 디렉토리에서 실행하지 않는 한. 어쨌든 이것은 게시 된 다른 솔루션보다 파일 시스템을 다시 순회하는 CPU 시간이 훨씬 적습니다.

샘플 출력 :

...
            2429             76732484 /home/peter/weird-filenames/test/.hiddendir/foo bar
            2429             76732484 /home/peter/weird-filenames/test.orig/.hiddendir/foo bar

            2430             17961006 /usr/bin/pkg-config.real
            2430             17961006 /usr/bin/x86_64-pc-linux-gnu-pkg-config

            2430             36646920 /usr/lib/i386-linux-gnu/dri/i915_dri.so
            2430             36646920 /usr/lib/i386-linux-gnu/dri/i965_dri.so
            2430             36646920 /usr/lib/i386-linux-gnu/dri/nouveau_vieux_dri.so
            2430             36646920 /usr/lib/i386-linux-gnu/dri/r200_dri.so
            2430             36646920 /usr/lib/i386-linux-gnu/dri/radeon_dri.so
...

TODO ?: awk또는로 출력을 채 웁니다 cut. uniq필드 선택 지원이 매우 제한되어 있으므로 찾기 출력을 채우고 고정 너비를 사용합니다. 20chars는 가능한 최대 inode 또는 장치 번호 (2 ^ 64-1 = 18446744073709551615)에 대해 충분히 넓습니다. XFS는 디스크에서 할당 된 위치를 기준으로 inode 번호를 0에서 연속적으로 선택하지 않으므로 수십억 개의 파일이없는 경우에도 큰 XFS 파일 시스템의> 32 비트 inode 번호를 가질 수 있습니다. 다른 파일 시스템은 거대하지 않더라도 20 자리의 inode 번호를 가질 수 있습니다.

TODO : 경로별로 중복 그룹을 정렬합니다. 마운트 포인트를 기준으로 정렬 한 다음 inode 번호를 사용하면 하드 링크가 많은 두 개의 서로 다른 하위 디렉토리가있는 경우 여러 항목을 혼합합니다. (즉, dup-group의 그룹은 함께 진행되지만 출력이 혼합됩니다).

마지막 sort -k 3은 줄 그룹을 단일 레코드로 분류하지 않고 개별적으로 줄을 정렬합니다. 줄 바꿈 쌍을 NUL 바이트로 변환하기 위해 무언가를 사전 처리하고 GNU를 사용 sort --zero-terminated -k 3하면 트릭을 수행 할 수 있습니다. tr그러나 2-> 1 또는 1-> 2 패턴이 아닌 단일 문자에서만 작동합니다. perl그것을 할 것입니다 (또는 단지 perl 또는 awk 내에서 구문 분석하고 정렬하십시오). sed작동 할 수도 있습니다.


1
%D파일 시스템 식별자 (파일 시스템이없는 동안 현재 부팅에 대해 고유함 umount)이므로보다 일반적인 내용은 다음과 같습니다 find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate. 이것은 주어진 디렉토리에 파일 시스템 수준의 다른 디렉토리가 포함되어 있지 않은 한 작동합니다. 또한 장치 또는 소프트 링크와 같이 하드 링크 할 수있는 모든 것을 찾습니다. 그 주 dev_tino_t오늘 64 비트 길이이다. 우리가 64 비트 시스템을 가지고있는 한 이것은 유지 될 것입니다.
티노

@Tino : ! -type d대신을 사용하는 것이 좋습니다 -type f. 파일 시스템에 일부 파일 모음을 구성하지 못하도록 하드 링크 된 심볼릭 링크가 있습니다. 개선 된 버전으로 내 대답을 업데이트했습니다 (하지만 fs-id를 먼저 배치하면 정렬 순서가 파일 시스템별로 그룹화됩니다)
Peter Cordes

3

이것은 Torocoro-Macho 자신의 답변과 스크립트에 대한 주석이지만 주석 상자에는 맞지 않습니다.


정보를 찾을 수있는보다 간단한 방법으로 스크립트를 다시 작성하여 프로세스 호출을 줄였습니다.

#!/bin/sh
xPATH=$(readlink -f -- "${1}")
for xFILE in "${xPATH}"/*; do
    [ -d "${xFILE}" ] && continue
    [ ! -r "${xFILE}" ] && printf '"%s" is not readable.\n' "${xFILE}" 1>&2 && continue
    nLINKS=$(stat -c%h "${xFILE}")
    if [ ${nLINKS} -gt 1 ]; then
        iNODE=$(stat -c%i "${xFILE}")
        xDEVICE=$(stat -c%m "${xFILE}")
        printf '\nItem: %s[%d] = %s\n' "${xDEVICE}" "${iNODE}" "${xFILE}";
        find "${xDEVICE}" -inum ${iNODE} -not -path "${xFILE}" -printf '     -> %p\n' 2>/dev/null
    fi
done

쉬운 비교를 위해 가능한 한 유사하게 유지하려고했습니다.

이 스크립트와 당신에 대한 의견

  • $IFS글로브가 불필요하게 복잡하고 파일 이름에 실제로 줄 바꿈이 포함될 수 있기 때문에 글로브가 충분하다면 항상 마법을 피해야합니다 (실제로는 대부분 첫 번째 이유).

  • ls조만간 물릴 것이기 때문에 가능한 수동으로 구문 분석 및 출력을 피해야 합니다. 예를 들어 awk, 첫 번째 줄에서 공백이 포함 된 모든 파일 이름에서 실패합니다.

  • printf%s구문 이 너무 강력하기 때문에 종종 문제를 해결 합니다. 또한 출력을 완벽하게 제어 할 수 있으며와 달리 모든 시스템에서 일관 echo됩니다.

  • stat 이 경우 많은 논리를 저장할 수 있습니다.

  • GNU find 강력합니다.

  • 귀하 headtail호출은 직접 처리 할 수 있었던 awk예와 exit명령 및 / 또는에서 선택 NR변수입니다. 이렇게하면 프로세스 호출이 절약되어 근면 한 스크립트에서 성능이 거의 항상 향상됩니다.

  • 당신 egrep의 것뿐만 아니라 수 있습니다 grep.


xDEVICE = $ (stat -c % m "$ {xFILE}")가 모든 시스템에서 작동하지는 않습니다 (예 : stat (GNU coreutils) 6.12). 스크립트가 "Item :?"를 출력하는 경우 각 줄의 맨 앞에있는이 문제의 줄을 원래 스크립트와 비슷한 줄로 바꾸 되 xITEM의 이름을 xFILE로 바꿉니다. xDEVICE = $ (df "$ {xFILE}"| tail -1l | awk '{print $ 6} ')
kbulgrien

각 멤버를 "마스터"로 반복하지 않고 하드 링크 그룹 만 원하면을 사용하십시오 find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate. fs를 한 번만 통과하기 때문에 훨씬 빠릅니다. 한 번에 여러 FS의 경우, inode 번호 앞에 FS id를 붙여야합니다. 아마find -exec stat... -printf ...
Peter Cordes

그 아이디어를 답으로 바 꾸었습니다
Peter Cordes

2

findhardlinks스크립트를 기반으로 (으로 이름을 바 hard-links)) 이것이 리팩토링되어 작동하게합니다.

산출:

# ./hard-links /root

Item: /[10145] = /root/.profile
    -> /proc/907/sched
    -> /<some-where>/.profile

Item: /[10144] = /root/.tested
    -> /proc/907/limits
    -> /<some-where else>/.bashrc
    -> /root/.testlnk

Item: /[10144] = /root/.testlnk
    -> /proc/907/limits
    -> /<another-place else>/.bashrc
    -> /root/.tested

 

# cat ./hard-links
#!/bin/bash
oIFS="${IFS}"; IFS=$'\n';
xPATH="${1}";
xFILES="`ls -al ${xPATH}|egrep "^-"|awk '{print $9}'`";
for xFILE in ${xFILES[@]}; do
  xITEM="${xPATH}/${xFILE}";
  if [[ ! -r "${xITEM}" ]] ; then
    echo "Path: '${xITEM}' is not accessible! ";
  else
    nLINKS=$(ls -ld "${xITEM}" | awk '{print $2}')
    if [ ${nLINKS} -gt 1 ]; then
      iNODE=$(ls -id "${xITEM}" | awk '{print $1}' | head -1l)
      xDEVICE=$(df "${xITEM}" | tail -1l | awk '{print $6}')
      echo -e "\nItem: ${xDEVICE}[$iNODE] = ${xITEM}";
      find ${xDEVICE} -inum ${iNODE} 2>/dev/null|egrep -v "${xITEM}"|sed 's/^/   -> /';
    fi
  fi
done
IFS="${oIFS}"; echo "";

이 스크립트에 대한 의견을 별도의 답변으로 게시했습니다.
Daniel Andersson 2012 년

1

GUI 솔루션은 실제로 귀하의 질문에 가깝습니다.

이전 주석자가 지적했듯이 "이름"파일은 동일한 데이터에 대한 별칭 일 뿐이므로 "ls"에서 실제 하드 링크 된 파일을 나열 할 수 없습니다. 그러나 실제로는 원하는 Linux 시스템에서 동일한 데이터 (하드 링크로)를 가리키는 파일 이름의 경로 목록을 표시하는 GUI 도구가 있습니다.이를 FSLint라고합니다. 원하는 옵션은 "이름 충돌"-> 검색 (XX)-> "확인란 $ PATH"선택 해제-> "중요 ..."다음에있는 드롭 다운 상자에서 "중첩"을 상단 중간으로 선택하십시오.

FSLint는 문서화가 잘되어 있지 않지만 "Recurse?"확인란이 선택된 "Search path"아래의 제한된 디렉토리 트리를 확인해야합니다. 전술 한 옵션, 동일한 데이터를 가리키는 "경로"와 이름을 가진 하드 링크 된 데이터의리스트는 프로그램이 검색된 후에 생성된다.



1

ls'별칭 (alias)'을 사용하여 하드 링크를 강조 표시 하도록 구성 할 수 있지만, 앞서 언급 한 것처럼 하드 링크의 '소스'를 표시 할 방법이 없기 때문에 .hardlink도움을주기 위해 추가 합니다.

하드 링크 강조 표시

당신의 어딘가에 다음을 추가하십시오 .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.