eu corro
ln /a/A /b/B
Gostaria de ver na pasta a
onde o arquivo A aponta para ls
.
eu corro
ln /a/A /b/B
Gostaria de ver na pasta a
onde o arquivo A aponta para ls
.
Respostas:
Você pode encontrar o número do inode para seu arquivo com
ls -i
e
ls -l
mostra a contagem de referências (número de hardlinks para um inode específico)
depois de encontrar o número do inode, você pode procurar todos os arquivos com o mesmo inode:
find . -inum NUM
mostrará os nomes dos arquivos para o inode NUM no diretório atual (.)
Não há realmente uma resposta bem definida para sua pergunta. Ao contrário dos links simbólicos, os links físicos são indistinguíveis do "arquivo original".
As entradas do diretório consistem em um nome de arquivo e um ponteiro para um inode. O inode, por sua vez, contém os metadados do arquivo e (indicadores para) o conteúdo real do arquivo). Criar um link físico cria outro nome de arquivo + referência para o mesmo inode. Essas referências são unidirecionais (pelo menos em sistemas de arquivos típicos) - o inode mantém apenas uma contagem de referência. Não há uma maneira intrínseca de descobrir qual é o nome do arquivo "original".
A propósito, é por isso que a chamada do sistema para "excluir" um arquivo é chamada unlink
. Apenas remove um hardlink. O inode e os dados anexados serão excluídos apenas se a contagem de referência do inode cair para 0.
A única maneira de encontrar as outras referências a um determinado inode é pesquisar exaustivamente no sistema de arquivos, verificando quais arquivos se referem ao inode em questão. Você pode usar 'test A -ef B' no shell para executar esta verificação.
O UNIX possui links físicos e simbólicos (criados com "ln"
e "ln -s"
respectivamente). Links simbólicos são simplesmente um arquivo que contém o caminho real para outro arquivo e pode atravessar sistemas de arquivos.
Os hard links existem desde os primeiros dias do UNIX (que eu me lembro de qualquer maneira, e isso já faz um bom tempo). São duas entradas de diretório que referenciam exatamente os mesmos dados subjacentes. Os dados em um arquivo são especificados por seus inode
. Cada arquivo em um sistema de arquivos aponta para um inode, mas não é necessário que cada arquivo aponte para um inode exclusivo - é daí que os links físicos vêm.
Como os inodes são exclusivos apenas para um determinado sistema de arquivos, há uma limitação de que os links físicos devem estar no mesmo sistema de arquivos (diferente dos links simbólicos). Observe que, diferentemente dos links simbólicos, não há arquivo privilegiado - todos são iguais. A área de dados será liberada somente quando todos os arquivos que usam esse inode forem excluídos (e todos os processos também serão fechados, mas esse é um problema diferente).
Você pode usar o "ls -i"
comando para obter o inode de um arquivo específico. Você pode usar o "find <filesystemroot> -inum <inode>"
comando para encontrar todos os arquivos no sistema de arquivos com o inode fornecido.
Aqui está um script que faz exatamente isso. Você o invoca com:
findhardlinks ~/jquery.js
e encontrará todos os arquivos nesse sistema de arquivos que são links físicos para esse arquivo:
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
Aqui está o script.
#!/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
. ./findhardlinks.bash
estar no Zsh do OS X. Minha janela atual na tela se fecha.
INUM=$(stat -c %i $1)
. Também NUM_LINKS=$(stat -c %h $1)
. Veja man stat
para obter mais variáveis de formato que você pode usar.
ls -l
A primeira coluna representará permissões. A segunda coluna será o número de subitens (para diretórios) ou o número de caminhos para os mesmos dados (links físicos, incluindo o arquivo original) para o arquivo. Por exemplo:
-rw-r--r--@ 2 [username] [group] [timestamp] HardLink
-rw-r--r--@ 2 [username] [group] [timestamp] Original
^ Number of hard links to the data
inode
que, por sua vez, aponta para o conteúdo do disco.
Que tal o seguinte, mais simples? (Os últimos podem substituir os scripts longos acima!)
Se você possui um arquivo específico <THEFILENAME>
e deseja conhecer todos os seus hardlinks espalhados pelo diretório <TARGETDIR>
, (que pode até ser todo o sistema de arquivos indicado por /
)
find <TARGETDIR> -type f -samefile <THEFILENAME>
Estendendo a lógica, se você quiser conhecer todos os arquivos nos <SOURCEDIR>
vários links físicos espalhados <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
porque o arquivo também pode ser um diretório.
.
e ..
nos diretórios são hardlinks. Você pode dizer quantos subdiretórios estão em um diretório a partir da contagem de links de .
. Isso é discutível de qualquer maneira, já que find -samefile .
ainda não imprimirá nenhuma subdir/..
saída. find
(pelo menos a versão GNU) parece estar codificado para ignorar ..
, mesmo com -noleaf
.
O(n^2)
executada find
uma vez para cada membro de um conjunto de arquivos vinculados. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate
iria funcionar, (16 não é grande o suficiente para uma representação decimal de 2 ^ 63-1, então quando seu sistema de arquivos XFS é grande o suficiente para ter números de inode que a alta, atente)
Existem muitas respostas com scripts para encontrar todos os hardlinks em um sistema de arquivos. A maioria deles faz coisas tolas, como a execução de localizar, para verificar todo o sistema de arquivos em -samefile
busca de CADA arquivo vinculado à multiplicação. Isso é loucura; tudo o que você precisa é escolher o número do inode e imprimir duplicatas.
Com apenas uma passagem pelo sistema de arquivos para encontrar e agrupar todos os conjuntos de arquivos vinculados
find dirs -xdev \! -type d -links +1 -printf '%20D %20i %p\n' |
sort -n | uniq -w 42 --all-repeated=separate
Isso é muito mais rápido que as outras respostas para encontrar vários conjuntos de arquivos com links físicos.
find /foo -samefile /bar
é excelente para apenas um arquivo.
-xdev
: limite para um sistema de arquivos. Não é estritamente necessário, pois também imprimimos o FS-id para uniq em! -type d
diretórios de rejeição: as entradas .
e ..
significam que elas estão sempre vinculadas.-links +1
: contagem de links estritamente > 1
-printf ...
imprima o ID do FS, o número do inode e o caminho. (Com preenchimento para larguras de colunas fixas que podemos saber uniq
.)sort -n | uniq ...
classificação numérica e unificar nas 42 primeiras colunas, separando grupos com uma linha em brancoUsar ! -type d -links +1
significa que a entrada da classificação é apenas tão grande quanto a saída final do uniq, portanto não estamos fazendo uma grande quantidade de classificação de string. A menos que você o execute em um subdiretório que contenha apenas um de um conjunto de links físicos. De qualquer forma, isso consumirá muito menos tempo de CPU ao atravessar o sistema de arquivos do que qualquer outra solução publicada.
saída de amostra:
...
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?: Desmarque a saída com awk
ou cut
. uniq
tem suporte de seleção de campo muito limitado, por isso, preencha a saída de localização e uso a largura fixa. 20chars é grande o suficiente para o número máximo possível de inode ou dispositivo (2 ^ 64-1 = 18446744073709551615). O XFS escolhe números de inodes com base em onde eles estão alocados no disco, e não contíguamente de 0, para que sistemas de arquivos XFS grandes possam ter números de inode> 32 bits, mesmo que não tenham bilhões de arquivos. Outros sistemas de arquivos podem ter números de inode de 20 dígitos, mesmo que não sejam gigantescos.
TODO: classifica grupos de duplicatas por caminho. Classificá-los por ponto de montagem, em seguida, o número do inode combina tudo, se você tiver alguns subdiretórios diferentes que possuem muitos hardlinks. (ou seja, grupos de grupos duplos andam juntos, mas a saída os mistura).
Uma final sort -k 3
classificaria as linhas separadamente, não grupos de linhas como um único registro. O pré-processamento de algo para transformar um par de novas linhas em um byte NUL, e o uso do GNU sort --zero-terminated -k 3
pode ajudar. tr
só opera com caracteres únicos, mas não com padrões 2-> 1 ou 1-> 2. perl
faria isso (ou apenas analisaria e classificaria dentro de perl ou awk). sed
também pode funcionar.
%D
é o identificador de sistema de arquivos (que é único para a inicialização atual, enquanto há sistemas de arquivos são umount
ed), de modo que se segue é ainda mais genérica: find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate
. Isso funciona desde que nenhum diretório contenha outro diretório no nível do sistema de arquivos, mas também analisa tudo o que pode ser vinculado por hardware (como dispositivos ou softlinks - sim, os softlinks podem ter uma contagem de links maior que 1). Note que dev_t
e ino_t
tem 64 bits hoje. Isso provavelmente será válido enquanto tivermos sistemas de 64 bits.
! -type d
, em vez de -type f
. Eu até tenho alguns links simbólicos vinculados no meu sistema de arquivos ao organizar algumas coleções de arquivos. Atualizei a minha resposta com a sua versão melhorada (mas eu coloquei o fs-id primeiro, então a ordem de classificação, pelo menos, grupos de sistema de arquivos.)
Isso é um comentário à própria resposta e roteiro de Torocoro-Macho, mas obviamente não cabe na caixa de comentários.
Reescreva seu script com maneiras mais simples de encontrar as informações e, portanto, muito menos invocações de processos.
#!/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
Tentei mantê-lo o mais semelhante possível ao seu para facilitar a comparação.
Deve-se sempre evitar a $IFS
magia se uma glob é suficiente, uma vez que é desnecessariamente complicada, e os nomes dos arquivos podem realmente conter novas linhas (mas, na prática, principalmente a primeira razão).
Você deve evitar a análise manual ls
e essa saída, tanto quanto possível, pois mais cedo ou mais tarde você será mordido. Por exemplo: na sua primeira awk
linha, você falha em todos os nomes de arquivos que contêm espaços.
printf
muitas vezes salvará problemas no final, pois é muito robusto com a %s
sintaxe. Também oferece controle total sobre a saída e é consistente em todos os sistemas, ao contrário echo
.
stat
pode economizar muita lógica nesse caso.
GNU find
é poderoso.
Suas invocações head
e tail
poderiam ter sido tratadas diretamente awk
com, por exemplo, o exit
comando e / ou a seleção na NR
variável. Isso salvaria as invocações do processo, que quase sempre melhoram o desempenho severamente em scripts de trabalho árduo.
Seus egrep
s poderiam muito bem ser justos grep
.
find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate
. Isso é MUITO mais rápido, pois ele atravessa o fs apenas uma vez. Para vários FSes de uma só vez, você precisa prefixar os números dos inodes com um ID de FS. Talvez comfind -exec stat... -printf ...
Baseado no findhardlinks
script (renomeado para hard-links
), é isso que refatorei e fiz funcionar.
Resultado:
# ./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 "";
Uma solução GUI fica muito próxima da sua pergunta:
Você não pode listar os arquivos vinculados reais de "ls" porque, como os comentaristas anteriores apontaram, os "nomes" do arquivo são meros aliases para os mesmos dados. No entanto, na verdade, existe uma ferramenta GUI que se aproxima muito do que você deseja, que é exibir uma lista de caminhos de nomes de arquivos que apontam para os mesmos dados (como hardlinks) no linux, chamada FSLint. A opção desejada está em "Confrontos de nomes" -> desmarque "caixa de seleção $ PATH" em Pesquisa (XX) -> e selecione "Aliases" na caixa suspensa após "para ..." na parte superior central.
O FSLint está muito mal documentado, mas descobri que a árvore de diretórios limitada em "Caminho de pesquisa" está marcada com a caixa de seleção selecionada para "Recurso?" e as opções acima mencionadas, uma lista de dados vinculados com caminhos e nomes que "apontam" para os mesmos dados são produzidos após a pesquisa do programa.
Você pode configurar ls
para destacar os hardlinks usando um 'alias', mas, como afirmado anteriormente, não há como mostrar a 'fonte' do hardlink, e é por isso que anexo .hardlink
para ajudá-lo.
Adicione o seguinte em algum lugar do seu .bashrc
alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
link(2)
chamada do sistema, não há sentido em qual é o original e o que é o link. É por isso que, como as respostas apontam, a única maneira de encontrar todos os links éfind / -samefile /a/A
. Como uma entrada de diretório para um inode não "conhece" outras entradas de diretório para o mesmo inode. Tudo o que eles fazem é refcount o inode para que ele possa ser excluído quando o sobrenome forunlink(2)ed
. (Esta é a "contagem de links" nals
saída).