it-swarm.dev

Como você pode ver o hard link real por ls?

Eu corro

ln /a/A /b/B

Eu gostaria de ver na pasta a onde o arquivo A aponta para ls.

91
Léo Léopold Hertz 준영

Você pode encontrar o número de inode para o seu arquivo com

ls -i

e

ls -l

mostra a contagem de referências (número de hardlinks para um determinado inode)

depois que você encontrou o número inode, você pode procurar por todos os arquivos com o mesmo inode:

find . -inum NUM

mostrará nomes de arquivos para inode NUM no diretório atual (.)

163
zzr

Não há realmente uma resposta bem definida para sua pergunta. Ao contrário dos links simbólicos, os hardlinks são indistinguíveis do "arquivo original".

Entradas de 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 (ponteiros 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 (em sistemas de arquivos típicos, pelo menos) - o inode mantém apenas uma contagem de referência. Não existe uma maneira intrínseca de descobrir qual é o nome do arquivo "original".

By the way, é por isso que a chamada do sistema para "excluir" um arquivo é chamado unlink. Apenas remove um hardlink. O inode e os dados anexados são excluídos somente se a contagem de referência do inode cair para 0.

A única maneira de encontrar as outras referências a um inode dado é pesquisar exaustivamente sobre o sistema de arquivos, verificando quais arquivos se referem ao inode em questão. Você pode usar 'test A -ef B' no Shell para executar essa verificação.

60
Laurence Gonsalves

O UNIX possui links físicos e links simbólicos (feitos com "ln" e "ln -s" respectivamente). Os links simbólicos são simplesmente um arquivo que contém o caminho real para outro arquivo e pode cruzar sistemas de arquivos.

Hiperligações duras existem desde os primeiros dias do UNIX (que me lembro de qualquer maneira, e isso está de volta há algum tempo). São duas entradas de diretório que fazem referência a exato mesmos dados subjacentes. Os dados em um arquivo são especificados por seu inode. Cada arquivo em um sistema de arquivos aponta para um inode, mas não há exigência de que cada arquivo aponte para um inode exclusivo - é de onde vêm os links físicos.

Como os inodes são exclusivos apenas para um determinado sistema de arquivos, existe uma limitação de que os hard links devem estar no mesmo sistema de arquivos (ao contrário dos links simbólicos). Note que, ao contrário dos links simbólicos, não há arquivo privilegiado - eles são todos iguais. A área de dados só será liberada quando todos os arquivos que usam esse inode forem deletados (e todos os processos forem fechados também, mas isso é um problema diferente).

Você pode usar o comando "ls -i" para obter o inode de um arquivo específico. Você pode então usar o comando "find <filesystemroot> -inum <inode>" para encontrar todos os arquivos no sistema de arquivos com o inode fornecido.

Aqui está um script que faz exatamente isso. Você invoca com:

findhardlinks ~/jquery.js

e encontrará todos os arquivos nesse sistema de arquivos que são links físicos para esse arquivo:

[email protected]:~# ./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 roteiro.

#!/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
33
user53528
ls -l

A primeira coluna representará as 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) no arquivo. Por exemplo:

[email protected]    2    [username]    [group]    [timestamp]     HardLink
[email protected]    2    [username]    [group]    [timestamp]     Original
               ^ Number of hard links to the data
24
eyelidlessness

Como sobre o seguinte mais simples? (Último pode substituir os scripts longos acima!)

Se você tem um arquivo específico <THEFILENAME>e quer saber todos os seus hardlinks espalhados pelo diretório <TARGETDIR>, (que pode até ser o sistema de arquivos inteiro denotado por /)

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

Estendendo a lógica, se você quiser saber todos os arquivos no <SOURCEDIR> com vários hard-links distribuídos por <TARGETDIR>:

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

Há muitas respostas com scripts para encontrar todos os hardlinks em um sistema de arquivos. A maioria faz coisas bobas como executar find para escanear todo o sistema de arquivos para -samefile para CADA arquivo multi-linkado. Isso é loucura; tudo o que você precisa é classificar o número de inode e imprimir as duplicatas.

find directories.. -xdev ! -type d -links +1 -printf '%20D %20i %p\n' | sort -n | uniq -w 42 --all-repeated=separate (Obrigado ao @Tino por mexer no meu comando original para suportar um FS-id (%D), e para lidar com todos os tipos de arquivos não-diretórios, não apenas arquivos regulares. Isto irá encontrar seus links simbólicos, pipes, etc.)

Usando ! -type d -links +1 significa que a entrada desse tipo é tão grande quanto a saída final do uniq. A menos que você o execute em um subdiretório que contenha apenas um conjunto de hardlinks. De qualquer forma, isso usará muito menos tempo de CPU para recuperar o sistema de arquivos do que qualquer outra solução publicada.

amostra de saída:

...
            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 ?: des-preenche a saída. uniq tem suporte de seleção de campo muito limitado, portanto, preenho a saída de localização e uso de largura fixa. 20chars é grande o suficiente para o máximo possível inode ou número do dispositivo (2 ^ 64-1 = 18446744073709551615). O XFS escolhe os números de inode com base no local em que eles estão alocados, não contiguamente a partir de 0, portanto sistemas de arquivos XFS grandes podem ter números de inodes de 32 bits, mesmo se não tiverem bilhões de arquivos. Outros sistemas de arquivos podem ter números de inodes de 20 dígitos, mesmo que não sejam gigantescos.

TODO: classifica grupos de duplicatas por caminho. Tendo eles classificados por ponto de montagem, o número de inode mistura as coisas juntas, se você tiver um par de subdiretórios diferentes que tenham muitos hardlinks. (isto é, grupos de grupos dup vão juntos, mas a saída os mistura).

Um sort -k 3 final classificaria as linhas separadamente, e não grupos de linhas como um único registro. O pré-processamento com algo para transformar um par de novas linhas em um byte NUL, e usando GNU sort --zero-terminated -k 3 pode fazer o truque. tr só opera em caracteres únicos, não em padrões 2-> 1 ou 1-> 2. Perl faria isso (ou apenas analisar e classificar dentro de Perl ou awk). sed também pode funcionar.

4
Peter Cordes

Isto é um pouco de um comentário para a resposta e roteiro do próprio Torocoro-Macho, mas obviamente não caberá na caixa de comentários.


Reescreva seu script com maneiras mais diretas de encontrar as informações e, portanto, muito menos invocações de processo.

#!/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 tão parecido com o seu quanto possível para facilitar a comparação.

Comentários sobre este roteiro e o seu

  • Deve-se sempre evitar a magia $IFS se um glob é suficiente, uma vez que é desnecessariamente complicado, e os nomes dos arquivos podem conter novas linhas (na prática, principalmente, o primeiro motivo).

  • Você deve evitar a análise manual de ls e tal saída, tanto quanto possível, uma vez que mais cedo ou mais tarde vai mordê-lo. Por exemplo: em sua primeira linha awk, você falha em todos os nomes de arquivos que contêm espaços.

  • printf frequentemente salvará os problemas no final, já que é tão robusto com a sintaxe %s. Ele também fornece controle total sobre a saída e é consistente em all systems, ao contrário de echo.

  • stat pode poupar muita lógica neste caso.

  • GNU find é poderoso.

  • Suas invocações head e tail poderiam ter sido tratadas diretamente em awk com, por exemplo, o comando exit e/ou selecionando a variável NR. Isso economizaria invocações de processo, o que quase sempre melhora significativamente o desempenho em scripts que trabalham duro.

  • Seu egreps poderia ser apenas grep.

3
Daniel Andersson

Com base no script findhardlinks (renomeado para hard-links), é o que refatorei e fiz com que funcionasse.

Saída:

# ./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 "";
2
Torocoro-Macho

Uma solução de GUI chega muito perto de sua pergunta:

Você não pode listar os arquivos com link real a partir de "ls" porque, como comentadores anteriores apontaram, o arquivo "nomes" são meros aliases para os mesmos dados. No entanto, há realmente uma ferramenta GUI que fica realmente perto do que você quer, que é para exibir uma lista de caminhos de nomes de arquivos que apontam para os mesmos dados (como hardlinks) no Linux, é chamado FSLint. A opção que você deseja está em "Conflitos de nome" -> desmarque "caixa de seleção $ PATH" em Pesquisar (XX) -> e selecione "Aliases" na caixa suspensa depois de "para ..." em direção ao meio superior.

O FSLint está muito mal documentado, mas descobri que certificar a árvore de diretórios limitada em "Caminho de pesquisa" com a caixa de seleção marcada para "Recurse?" e as opções acima mencionadas, uma listagem de dados com link físico com caminhos e nomes que "apontam" para os mesmos dados são produzidos após as procuras do programa.

1
Charles

Você pode configurar ls para destacar hardlinks usando um 'alias', mas como dito antes, não há como mostrar a 'fonte' do hardlink, e é por isso que eu adiciono .hardlink para ajudar com isso.

highlight hardlinks

Adicione o seguinte em algum lugar no seu .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
1
Daniel Sokolowski