난 달린다
ln /a/A /b/B
a
파일 A가 가리키는 폴더를보고 싶습니다 ls
.
난 달린다
ln /a/A /b/B
a
파일 A가 가리키는 폴더를보고 싶습니다 ls
.
답변:
파일의 inode 번호를 찾을 수 있습니다
ls -i
과
ls -l
참조 횟수를 표시합니다 (특정 inode에 대한 하드 링크 수)
inode 번호를 찾은 후 동일한 inode를 가진 모든 파일을 검색 할 수 있습니다.
find . -inum NUM
현재 디렉토리 (.)에 inode NUM의 파일 이름을 표시합니다.
귀하의 질문에 대한 명확한 대답이 없습니다. 심볼릭 링크와 달리 하드 링크는 "원본 파일"과 구별 할 수 없습니다.
디렉토리 항목은 파일 이름과 inode에 대한 포인터로 구성됩니다. inode에는 파일 메타 데이터와 실제 파일 내용을 가리키는 포인터가 포함됩니다. 하드 링크를 만들면 동일한 inode에 대한 다른 파일 이름 + 참조가 생성됩니다. 이러한 참조는 단방향 (일반적인 파일 시스템에서는)이며 inode는 참조 카운트 만 유지합니다. "original"파일 이름을 찾는 고유 한 방법은 없습니다.
그건 그렇고, 파일을 "삭제"하는 시스템 호출이 호출되는 이유 unlink
입니다. 하드 링크 만 제거합니다. 첨부 된 데이터는 inode의 참조 카운트가 0으로 떨어지는 경우에만 삭제됩니다.
주어진 inode에 대한 다른 참조를 찾는 유일한 방법은 문제의 inode를 참조하는 파일을 검사하는 파일 시스템을 철저히 검색하는 것입니다. 쉘에서 'test A -ef B'를 사용하여이 점검을 수행 할 수 있습니다.
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
INUM=$(stat -c %i $1)
입니다. 또한 NUM_LINKS=$(stat -c %h $1)
. man stat
사용할 수있는 더 많은 형식 변수를 참조하십시오 .
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
inode
디스크 내용을 가리킨다 는 점에서 동일하다 .
다음과 같은 간단한 것은 어떻습니까? 나중에 위의 긴 스크립트를 대체 할 수 있습니다!
특정 파일이 <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
파일도 디렉토리 일 수 있기 때문에 사용하지 않을 것 입니다.
.
및 ..
항목은 하드 링크입니다. 의 링크 수에서 디렉토리에 몇 개의 하위 디렉토리가 있는지 알 수 있습니다 .
. 어쨌든 출력물을 find -samefile .
인쇄하지 않기 때문에 어리석은 짓입니다 subdir/..
. find
(최소한 GNU 버전)은 ..
조차도 무시하도록 하드 코딩 된 것 같습니다 -noleaf
.
O(n^2)
실행됩니다 find
. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate
(16은 2 ^ 63-1의 10 진수 표현을 위해 충분히 넓지 않기 때문에 XFS 파일 시스템이 inode 수를 크게하기에 충분히 크면 조심하십시오)
파일 시스템에서 모든 하드 링크를 찾기위한 스크립트에는 많은 답변이 있습니다. 대부분의 경우 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 +1
sort의 입력이 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
작동 할 수도 있습니다.
%D
파일 시스템 식별자 (파일 시스템이없는 동안 현재 부팅에 대해 고유함 umount
)이므로보다 일반적인 내용은 다음과 같습니다 find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate
. 이것은 주어진 디렉토리에 파일 시스템 수준의 다른 디렉토리가 포함되어 있지 않은 한 작동합니다. 또한 장치 또는 소프트 링크와 같이 하드 링크 할 수있는 모든 것을 찾습니다. 그 주 dev_t
와 ino_t
오늘 64 비트 길이이다. 우리가 64 비트 시스템을 가지고있는 한 이것은 유지 될 것입니다.
! -type d
대신을 사용하는 것이 좋습니다 -type f
. 파일 시스템에 일부 파일 모음을 구성하지 못하도록 하드 링크 된 심볼릭 링크가 있습니다. 개선 된 버전으로 내 대답을 업데이트했습니다 (하지만 fs-id를 먼저 배치하면 정렬 순서가 파일 시스템별로 그룹화됩니다)
이것은 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
강력합니다.
귀하 head
와 tail
호출은 직접 처리 할 수 있었던 awk
예와 exit
명령 및 / 또는에서 선택 NR
변수입니다. 이렇게하면 프로세스 호출이 절약되어 근면 한 스크립트에서 성능이 거의 항상 향상됩니다.
당신 egrep
의 것뿐만 아니라 수 있습니다 grep
.
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 ...
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 "";
GUI 솔루션은 실제로 귀하의 질문에 가깝습니다.
이전 주석자가 지적했듯이 "이름"파일은 동일한 데이터에 대한 별칭 일 뿐이므로 "ls"에서 실제 하드 링크 된 파일을 나열 할 수 없습니다. 그러나 실제로는 원하는 Linux 시스템에서 동일한 데이터 (하드 링크로)를 가리키는 파일 이름의 경로 목록을 표시하는 GUI 도구가 있습니다.이를 FSLint라고합니다. 원하는 옵션은 "이름 충돌"-> 검색 (XX)-> "확인란 $ PATH"선택 해제-> "중요 ..."다음에있는 드롭 다운 상자에서 "중첩"을 상단 중간으로 선택하십시오.
FSLint는 문서화가 잘되어 있지 않지만 "Recurse?"확인란이 선택된 "Search path"아래의 제한된 디렉토리 트리를 확인해야합니다. 전술 한 옵션, 동일한 데이터를 가리키는 "경로"와 이름을 가진 하드 링크 된 데이터의리스트는 프로그램이 검색된 후에 생성된다.
ls
'별칭 (alias)'을 사용하여 하드 링크를 강조 표시 하도록 구성 할 수 있지만, 앞서 언급 한 것처럼 하드 링크의 '소스'를 표시 할 방법이 없기 때문에 .hardlink
도움을주기 위해 추가 합니다.
당신의 어딘가에 다음을 추가하십시오 .bashrc
alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
link(2)
시스템 호출 후 어떤 것이 원래인지, 어떤 것이 링크인지는 의미가 없습니다. 그렇기 때문에 답변에서 알 수 있듯이 모든 링크를 찾는 유일한 방법은find / -samefile /a/A
입니다. inode에 대한 하나의 디렉토리 항목은 동일한 inode에 대한 다른 디렉토리 항목을 "알지"않기 때문입니다. 그들이하는 모든 것은 inode를 다시 계산하기 때문에 그 이름이이면 삭제 될 수 있습니다unlink(2)ed
. (ls
출력 의 "링크 수"입니다 .)