Como você pode ver o link real por ls?


97

eu corro

ln /a/A /b/B

Gostaria de ver na pasta aonde o arquivo A aponta para ls.


11
Links físicos não são indicadores, links simbólicos são. Eles são vários nomes para o mesmo arquivo (inode). Após uma 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 for unlink(2)ed. (Esta é a "contagem de links" na lssaída).
22815 Peter Cordes

@ PeterCordes: o refcount está realmente armazenado na entrada do hardlink? É isso que sua redação implica ("Tudo o que eles fazem é recontar o inode ...") Mas isso não faria sentido se os links não soubessem nada um do outro, pois quando um deles é atualizado, todos os outros precisam ser atualizado. Ou o refcount é armazenado no próprio inode? (Me perdoe se for uma pergunta idiota, eu me considero um novato e ainda estou aprendendo).
loneboat

11
A refcount é armazenada no inode, como você acabou descobrindo que deve ser o caso, por outros fatos. :) As entradas do diretório são nomeadas como ponteiros para inodes. Nós chamamos isso de "ligação direta" quando você tem vários nomes apontando para o mesmo inode.
Peter Cordes

Respostas:


171

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 (.)


46
você pode apenas executar encontrar. -samefile nome do arquivo
BeowulfNode42 25/11

11
@ BeowulfNode42 Este comando é ótimo, mas precisa pelo menos da pasta raiz compartilhada dos mesmos arquivos.
Itachi,

11
esta resposta fornece um pragmático "faça isso", mas sinto fortemente que @LaurenceGonsalves responde às perguntas "como" e / ou "por que".
Trevor Boyd Smith

65

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.


35
Isso significa que não existe um link físico para outro arquivo , pois o arquivo original também é um link físico; links físicos apontam para um local no disco .
jtbandes

12
@jtbandes: links físicos apontam para um inode que aponta para os dados reais.
dash17291

33

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

@ pax: Parece haver um bug no script. Começo por . ./findhardlinks.bashestar no Zsh do OS X. Minha janela atual na tela se fecha.

4
@Masi A questão é sua inicial. (igual ao comando de origem). Isso faz com que o comando exit 1 saia do seu shell. Use chmod a + x findhardlinks.bash em seguida, executá-lo com ./findhardlinks.bash ou usar o bash findhardlinks.bash
njsf


3
Para fazer isso programaticamente, é provavelmente mais resistente se você usar este em vez disso: INUM=$(stat -c %i $1). Também NUM_LINKS=$(stat -c %h $1). Veja man statpara obter mais variáveis ​​de formato que você pode usar.
Joe

Melhor resposta, de longe. Parabéns.
MariusMatutiae

24
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

2
Útil para determinar se um determinado arquivo possui [outros] links físicos, mas não ONDE estão.
mklement0

Além disso, não há distinção técnica entre um link físico e um arquivo original. Ambos são idênticos, pois simplesmente apontam para o inodeque, por sua vez, aponta para o conteúdo do disco.
guyarad 16/01

13

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 {} \; 

Esta é para mim a melhor resposta! mas eu não usaria -type fporque o arquivo também pode ser um diretório.
silvio

3
@ Silvio: Você só pode criar links físicos para arquivos , não para diretórios.
mklement0

@ mklement0: Você está certo!
silvio

As entradas .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.
Peter Cordes

Além disso, essa ideia de encontrar todos os links é O(n^2)executada finduma vez para cada membro de um conjunto de arquivos vinculados. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separateiria 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)
Peter Cordes

5

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 -samefilebusca 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 ddiretó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 branco

Usar ! -type d -links +1significa 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 awkou cut. uniqtem 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 3classificaria 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 3pode ajudar. trsó opera com caracteres únicos, mas não com padrões 2-> 1 ou 1-> 2. perlfaria isso (ou apenas analisaria e classificaria dentro de perl ou awk). sedtambém pode funcionar.


11
%Dé o identificador de sistema de arquivos (que é único para a inicialização atual, enquanto há sistemas de arquivos são umounted), 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_te ino_ttem 64 bits hoje. Isso provavelmente será válido enquanto tivermos sistemas de 64 bits.
Tino

@ Tino: ótimo ponto de usar ! -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.)
Peter Cordes

3

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.

Comentários sobre este script e o seu

  • Deve-se sempre evitar a $IFSmagia 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 lse essa saída, tanto quanto possível, pois mais cedo ou mais tarde você será mordido. Por exemplo: na sua primeira awklinha, você falha em todos os nomes de arquivos que contêm espaços.

  • printfmuitas vezes salvará problemas no final, pois é muito robusto com a %ssintaxe. 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 heade tailpoderiam ter sido tratadas diretamente awkcom, por exemplo, o exitcomando e / ou a seleção na NRvariável. Isso salvaria as invocações do processo, que quase sempre melhoram o desempenho severamente em scripts de trabalho árduo.

  • Seus egreps poderiam muito bem ser justos grep.


xDEVICE = $ (stat -c% m "$ {xFILE}") não funciona em todos os sistemas (por exemplo: stat (GNU coreutils) 6.12). Se o script exibir "Item:?" na frente de cada linha, substitua esta linha incorreta por uma linha mais parecida com o script original, mas com o xITEM renomeado para xFILE: xDEVICE = $ (df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien 28/03

Se você deseja apenas grupos de hardlinks, em vez de repetir com cada membro como "mestre", use 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 ...
Peter Cordes

transformou essa idéia em uma resposta
Peter Cordes

2

Baseado no findhardlinksscript (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 "";

Publiquei comentários sobre esse script como uma resposta separada.
Daniel Andersson

1

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.


FSlint podem ser encontradas em pixelbeat.org/fslint
mklement0

1

Você pode configurar lspara destacar os hardlinks usando um 'alias', mas, como afirmado anteriormente, não há como mostrar a 'fonte' do hardlink, e é por isso que anexo .hardlinkpara ajudá-lo.

destacar hardlinks

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'
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.