2009-07-25 09:41:26 +0000 2009-07-25 09:41:26 +0000
104
104

Como se pode ver a verdadeira ligação dura por ls?

Eu corro

ln /a/A /b/B

gostaria de ver na pasta a onde o ficheiro A aponta por ls.

Respostas (9)

182
182
182
2009-07-25 10:01:32 +0000

Pode encontrar o número inode para o seu ficheiro com

ls -i
ls -l

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

depois de encontrar o número inode, pode procurar todos os ficheiros com o mesmo inode:

find . -inum NUM

mostrará nomes de ficheiros para o inode NUM no dir actual (.)

66
66
66
2009-07-25 09:51:57 +0000

Não há realmente uma resposta bem definida para a sua pergunta. Ao contrário dos links simbólicos, os links rígidos são indistinguíveis do “ficheiro original”.

As entradas de directório consistem num nome de ficheiro e num ponteiro para um inode. O inode, por sua vez, contém os metadados do ficheiro e (apontadores para) o conteúdo real do ficheiro). A criação de uma ligação dura cria outro nome de ficheiro + referência ao mesmo inode. Estas referências são unidireccionais (em sistemas de ficheiros típicos, pelo menos) – o inode mantém apenas uma contagem de referência. Não há nenhuma forma intrínseca de descobrir qual é o nome do ficheiro “original”.

A propósito, é por isso que a chamada do sistema para “apagar” um ficheiro é chamada unlink. Apenas remove uma ligação dura. O inode um dado anexo só é apagado se a contagem de referências do inode cair para 0.

A única maneira de encontrar as outras referências a um determinado inode é procurar exaustivamente no sistema de ficheiros verificando quais os ficheiros que se referem ao inode em questão. Pode utilizar o ‘teste A -ef B’ da concha para efectuar esta verificação.

24
24
24
2009-07-25 10:01:38 +0000
ls -l

A primeira coluna representará as permissões. A segunda coluna será o número de subitens (para directórios) ou o número de caminhos para os mesmos dados (ligações rígidas, incluindo o ficheiro original) para o ficheiro. 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
14
14
14
2013-08-15 08:52:16 +0000

Que tal o seguinte, mais simples? (A última pode substituir os scripts longos acima!)

Se tiver um ficheiro específico <THEFILENAME> e quiser conhecer todos os seus hardlinks espalhados pelo directório <TARGETDIR>, (que pode mesmo ser todo o sistema de ficheiros denotado por /)

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

Estendendo a lógica, se quiser conhecer todos os ficheiros no <SOURCEDIR> tendo múltiplos hard-links espalhados por <TARGETDIR>:

find <SOURCEDIR> -type f -links +1 \
  -printf "\n\n %n HardLinks of file : %H/%f \n" \
  -exec find <TARGETDIR> -type f -samefile {} \;
6
6
6
2015-04-21 19:32:47 +0000

Há muitas respostas com scripts para encontrar todos os hardlinks num sistema de ficheiros. A maioria deles faz coisas tolas como correr o find find para procurar 0x6 em todo o sistema de ficheiros& para CADA ficheiro multi-linked. Isto é uma loucura; tudo o que precisa é de ordenar no número de inode e imprimir duplicados.

Com apenas uma passagem sobre o sistema de ficheiros para encontrar e agrupar todos os conjuntos de ficheiros interligados

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

Isto é muito mais rápido do que as outras respostas* para encontrar múltiplos conjuntos de ficheiros interligados.
-samefile é excelente para apenas um ficheiro.

  • find /foo -samefile /bar : limite para um único sistema de ficheiros. Não é estritamente necessário uma vez que também imprimimos o FS-id para uniq em
  • -xdev: as entradas ! -type d e . significam que estão sempre ligadas.
  • .. : contagem estritamente -links +1 -
  • > 1 imprimir o FS-id, número de inode, e caminho. (Com acolchoamento para larguras fixas de colunas que podemos dizer -printf ...& sobre.)
  • uniq ordenação numérica e singular nas primeiras 42 colunas, separando grupos com uma linha em branco

Usando sort -n | uniq ... significa que a entrada da ordenação é apenas tão grande como a saída final do uniq, por isso não estamos a fazer uma grande quantidade de ordenação de cordas. A menos que seja executado num subdirectório que contenha apenas um de um conjunto de hardlinks. De qualquer forma, isto irá usar um LOT menos tempo de CPU a re-gravar o sistema de ficheiros 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?: des-padar a saída com ! -type d -links +1 ou awk. cut tem um suporte de selecção de campo muito limitado, por isso, eu acolchoo a saída de pesquisa e uso largura fixa. 20 caracteres é suficientemente largo para o máximo inode ou número de dispositivo possível (2^64-1 = 18446744073709551615). O XFS escolhe números de inode com base em onde são atribuídos no disco, não contíguamente a partir de 0, pelo que os grandes sistemas de ficheiros XFS podem ter >32bit de números de inode mesmo que não tenham milhares de milhões de ficheiros. Outros sistemas de ficheiros podem ter números de inode de 20 dígitos, mesmo que não sejam gigantescos.

TODO: ordenar grupos de duplicados por caminho. Tendo-os ordenados por ponto de montagem, então o número de inode mistura coisas, se tiver um par de subdires diferentes que tenham muitos hardlinks. (isto é, grupos de dup-groups vão juntos, mas a saída mistura-os).

Um último uniq classificaria as linhas separadamente, não os grupos de linhas como um único registo. O pré-processamento com algo para transformar um par de linhas novas num byte NUL, e a utilização do GNU sort -k 3 poderia fazer o truque. sort --zero-terminated -k 3 funciona apenas com caracteres únicos, e não com padrões 2->1 ou 1->2. tr fá-lo-ia (ou apenas analisaria e classificaria dentro do perl ou do awk). perl poderia também funcionar.

3
3
3
2012-06-13 07:40:43 +0000

Este é de certa forma um comentário à própria resposta e guião de Torocoro-Macho, mas obviamente não caberá na caixa de comentários.


Re-escreveu o seu guião com formas mais directas de encontrar a informação, e assim 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 parecido possível com o seu para facilitar a comparação.

Comentários sobre este script e o seu

  • Deve-se sempre evitar a magia $IFS se um globo for suficiente, uma vez que é desnecessariamente complicado, e os nomes dos ficheiros podem realmente conter novas linhas (mas na prática é sobretudo a primeira razão).

& - Deve-se evitar analisar manualmente ls& e tal saída tanto quanto possível, uma vez que, mais cedo ou mais tarde, ela morderá. Por exemplo: na sua primeira linha awk, falhará em todos os nomes de ficheiro que contenham espaços.

  • printf irá muitas vezes salvar problemas no final, uma vez que é tão robusto com a sintaxe %s. Também lhe dá controlo total sobre a saída, e é consistente em todos os sistemas, ao contrário do echo.

  • stat pode poupar-lhe muita lógica neste caso.

  • GNU find é poderoso.

  • As suas invocações head e tail poderiam ter sido tratadas directamente em awk com, por exemplo, o comando exit e/ou a selecção na variável NR. Isto pouparia invocações de processo, o que quase sempre aposta severamente no desempenho em scripts de trabalho árduo.

& - Os seus egrep&s poderiam muito bem ser apenas grep&.

2
2
2
2011-11-16 22:46:38 +0000

Com base no guião findhardlinks (rebaptizado para hard-links), foi isto que refiz e fiz com que funcionasse.

Output:

# ./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 "";
1
1
1
2015-01-20 18:00:05 +0000

Uma solução GUI aproxima-se realmente da sua pergunta:

Não é possível listar os ficheiros reais de “ls” porque, como comentadores anteriores assinalaram, os “nomes” dos ficheiros são meros pseudónimos para os mesmos dados. No entanto, existe realmente uma ferramenta GUI que se aproxima muito do que se pretende, que é exibir uma listagem de caminhos de nomes de ficheiros que apontam para os mesmos dados (como hardlinks) sob linux, chama-se FSLint. A opção que pretende está em “Name clashes” -> desmarque “checkbox $PATH” em Search (XX) -> e seleccione “Aliases” na caixa pendente depois de “for…” em direcção ao topo do meio.

FSLint está muito mal documentado, mas descobri que ao verificar a árvore de directórios limitada em “Search path” com a caixa de verificação seleccionada para “Recurse?” e as opções acima mencionadas, uma listagem de dados hardlinked com caminhos e nomes que “apontam” para os mesmos dados são produzidos após as pesquisas do programa.

1
1
1
2017-12-06 17:34:25 +0000

É possível configurar ls para realçar os hardlinks usando um ‘alias’, mas como foi dito antes, não há forma de mostrar a ‘fonte’ do hardlink, razão pela qual aponto .hardlink para ajudar com isso.

Adicione o seguinte algures no seu .bashrc

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