Come puoi vedere l'effettivo link reale di ls?


93

io corro

ln /a/A /b/B

Vorrei vedere nella cartella in acui il file A indica ls.


1
I collegamenti reali non sono puntatori, i collegamenti simbolici lo sono. Sono più nomi per lo stesso file (inode). Dopo una link(2)chiamata di sistema, non ha senso quale sia l'originale e uno sia il collegamento. Questo è il motivo per cui, come indicano le risposte, l'unico modo per trovare tutti i collegamenti è find / -samefile /a/A. Perché una voce di directory per un inode non "conosce" altre voci di directory per lo stesso inode. Tutto ciò che fanno è ricontattare l'inode in modo che possa essere eliminato quando il cognome è unlink(2)ed. (Questo è il "conteggio dei collegamenti" lsnell'output).
Peter Cordes,

@PeterCordes: il refcount è effettivamente memorizzato nella voce hardlink? Questo è ciò che implica la tua formulazione ("Tutto ciò che fanno è ricontattare l'inode ...") Ma non avrebbe senso se i collegamenti non si conoscessero a vicenda, dal momento che quando uno si aggiorna, tutti gli altri dovrebbero in qualche modo essere aggiornato. O il refcount è memorizzato nell'inode stesso? (Scusami se è una domanda stupida, mi considero un principiante e sto ancora imparando).
Loneboat,

1
Il refcount è memorizzato nell'inode, come alla fine hai capito, deve essere il caso, dagli altri fatti. :) Le voci della directory sono denominate puntatori agli inode. Lo chiamiamo "hard linking" quando hai più nomi che puntano allo stesso inode.
Peter Cordes,

Risposte:


166

Puoi trovare il numero di inode per il tuo file con

ls -i

e

ls -l

mostra il conteggio dei riferimenti (numero di hardlink per un particolare inode)

dopo aver trovato il numero dell'inode, è possibile cercare tutti i file con lo stesso inode:

find . -inum NUM

mostrerà i nomi dei file per l'inode NUM nella directory corrente (.)


45
potresti semplicemente scappare. -samefile nomefile
BeowulfNode42

1
@ BeowulfNode42 Questo comando è ottimo, ma necessita almeno della cartella radice condivisa degli stessi file.
Itachi,

1
questa risposta dà un pragmatico "fai questo" ma sento fermamente che @LaurenceGonsalves risponde alle domande "come" e / o "perché".
Trevor Boyd Smith,

63

Non c'è davvero una risposta ben definita alla tua domanda. A differenza dei symlink, i hardlink sono indistinguibili dal "file originale".

Le voci della directory sono costituite da un nome file e da un puntatore a un inode. L'inode a sua volta contiene i metadati del file e (puntatori a) il contenuto del file effettivo). La creazione di un collegamento reale crea un altro nome file + riferimento allo stesso inode. Questi riferimenti sono unidirezionali (almeno nei filesystem tipici) - l'inode mantiene solo un conteggio dei riferimenti. Non esiste un modo intrinseco per scoprire qual è il nome file "originale".

A proposito, questo è il motivo per cui viene chiamata la chiamata di sistema per "cancellare" un file unlink. Rimuove solo un hardlink. L'inode di un dato allegato viene cancellato solo se il conteggio di riferimento dell'inode scende a 0.

L'unico modo per trovare gli altri riferimenti a un dato inode è quello di effettuare una ricerca esaustiva sul file system verificando quali file si riferiscono all'inode in questione. È possibile utilizzare 'test A -ef B' dalla shell per eseguire questo controllo.


33
Ciò significa che non esiste un collegamento reale a un altro file , poiché anche il file originale è un collegamento reale; i collegamenti fissi indicano una posizione sul disco .
jtbandes,

10
@jtbandes: i collegamenti reali puntano a un inode che punta ai dati effettivi.
dash17291,

33

UNIX ha collegamenti fissi e collegamenti simbolici (creati con "ln"e "ln -s"rispettivamente). I collegamenti simbolici sono semplicemente un file che contiene il percorso reale di un altro file e può attraversare i filesystem.

I collegamenti reali sono in circolazione sin dai primi giorni di UNIX (che ricordo comunque, e che risale a parecchio tempo fa). Sono due voci di directory che fanno riferimento alle esatte dati stessi sottostanti. I dati in un file sono specificati dal suo inode. Ogni file su un file system punta a un inode ma non è necessario che ogni file faccia riferimento a un inode univoco: ecco da dove provengono i collegamenti reali.

Poiché gli inode sono univoci solo per un determinato file system, esiste una limitazione che i collegamenti fisici devono trovarsi sullo stesso file system (a differenza dei collegamenti simbolici). Si noti che, a differenza dei collegamenti simbolici, non esiste un file privilegiato: sono tutti uguali. L'area dati verrà rilasciata solo quando tutti i file che utilizzano quell'inode vengono eliminati (e anche tutti i processi lo chiudono, ma questo è un problema diverso).

È possibile utilizzare il "ls -i"comando per ottenere l'inode di un determinato file. È quindi possibile utilizzare il "find <filesystemroot> -inum <inode>"comando per trovare tutti i file sul filesystem con quel dato inode.

Ecco una sceneggiatura che fa esattamente questo. Lo invochi con:

findhardlinks ~/jquery.js

e troverà tutti i file su quel filesystem che sono hard link per quel file:

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

Ecco la sceneggiatura.

#!/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: sembra esserci un bug nello script. Lo avvio . ./findhardlinks.bashmentre mi trovo nella Zsh di OS X. La mia finestra corrente in Schermo si chiude.

3
@Masi Il problema è la tua iniziale. (uguale al comando sorgente). Questo fa sì che il comando exit 1 esca dalla shell. Usa chmod a + x findhardlinks.bash, quindi eseguilo con ./findhardlinks.bash o usa bash findhardlinks.bash
njsf,

Per favore, vedi la mia risposta alla tua risposta su superuser.com/questions/12972/to-see-hardlinks-by-ls/…
Léo Léopold Hertz

2
Per fare questo a livello di codice, è probabilmente più resistente se si utilizza questo invece: INUM=$(stat -c %i $1). Inoltre NUM_LINKS=$(stat -c %h $1). Vedi man statper altre variabili di formato che puoi usare.
Joe

La migliore risposta, di gran lunga. Complimenti.
MariusMatutiae,

24
ls -l

La prima colonna rappresenterà le autorizzazioni. La seconda colonna sarà il numero di voci secondarie (per le directory) o il numero di percorsi per gli stessi dati (collegamenti reali, incluso il file originale) al file. Per esempio:

-rw-r--r--@    2    [username]    [group]    [timestamp]     HardLink
-rw-r--r--@    2    [username]    [group]    [timestamp]     Original
               ^ Number of hard links to the data

2
Utile nel determinare SE un determinato file ha [altri] collegamenti reali, ma non DOVE sono.
mklement0,

Inoltre, non esiste alcuna distinzione tecnica tra un hard link e un file originale. Sono entrambi identici in quanto indicano semplicemente ciò inodeche a sua volta punta al contenuto del disco.
Guyarad,

12

Che ne dici del seguente più semplice? (Gli ultimi potrebbero sostituire i lunghi script sopra!)

Se hai un file specifico <THEFILENAME>e vuoi conoscere tutti i suoi hardlink sparsi sulla directory <TARGETDIR>(che può anche essere l'intero file system indicato da /)

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

Estensione della logica, se si desidera conoscere tutti i file <SOURCEDIR>con più collegamenti fissi distribuiti su <TARGETDIR>:

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

Questa è per me la migliore risposta! ma non lo userei -type fperché il file può essere anche una directory.
silvio,

2
@silvio: è possibile creare solo collegamenti reali ai file , non alle directory.
mklement0,

@ mklement0: hai ragione!
silvio,

Le voci .e ..nelle directory sono hardlink. Puoi sapere quanti sottodir sono in una directory dal conteggio dei collegamenti di .. Questo è comunque discutibile, dal momento che find -samefile .ancora non stampa alcun subdir/..output. find(almeno la versione GNU) sembra essere hardcoded da ignorare .., anche con -noleaf.
Peter Cordes,

inoltre, l'idea trova tutti i collegamenti è O(n^2), e viene eseguita finduna volta per ogni membro di una serie di file con collegamenti fissi. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separatefunzionerebbe, (16 non è abbastanza largo per una rappresentazione decimale di 2 ^ 63-1, quindi quando il tuo filesystem XFS è abbastanza grande da avere numeri di inode così alti, attenzione)
Peter Cordes,

5

Ci sono molte risposte con gli script per trovare tutti gli hardlink in un filesystem. La maggior parte di loro fa cose stupide come eseguire find per scansionare l'intero filesystem -samefileper OGNI file con collegamenti multipli. Questo è pazzesco; tutto ciò che serve è ordinare in base al numero di inode e stampare i duplicati.

Con un solo passaggio sul filesystem è possibile trovare e raggruppare tutti i set di file con collegamento fisico

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

Questo è molto più veloce delle altre risposte per trovare più set di file hardlinked.
find /foo -samefile /barè eccellente per un solo file.

  • -xdev: limite a un filesystem. Non strettamente necessario poiché stampiamo anche l'ID FS su uniq
  • ! -type drifiuta le directory: le voci .e ..indicano che sono sempre collegate.
  • -links +1 : conteggio dei collegamenti rigorosamente > 1
  • -printf ...stampa ID FS, numero di inode e percorso. (Con imbottitura a larghezze di colonna fisse di cui possiamo parlare uniq.)
  • sort -n | uniq ... ordinamento numerico e unificazione sulle prime 42 colonne, separando i gruppi con una riga vuota

Usare ! -type d -links +1significa che l'input di sort è grande quanto l'output finale di uniq, quindi non stiamo eseguendo un'enorme quantità di ordinamento delle stringhe. A meno che non lo si esegua in una sottodirectory che contiene solo uno di un set di hardlink. Ad ogni modo, ciò impiegherà MOLTO meno tempo della CPU a ri-attraversare il filesystem rispetto a qualsiasi altra soluzione pubblicata.

uscita campione:

...
            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 ?: decomprimere l'output con awko cut. uniqha un supporto per la selezione dei campi molto limitato, quindi utilizzo l'output di ricerca e utilizzo larghezza fissa. 20chars è abbastanza largo per l'inode massimo possibile o il numero del dispositivo (2 ^ 64-1 = 18446744073709551615). XFS sceglie i numeri di inode in base a dove sono allocati sul disco, non contigui da 0, quindi i filesystem XFS di grandi dimensioni possono avere numeri di inode> 32 bit anche se non hanno miliardi di file. Altri filesystem potrebbero avere numeri di inode di 20 cifre anche se non sono giganteschi.

TODO: ordina i gruppi di duplicati per percorso. Se li ordina in base al punto di montaggio, quindi il numero di inode mescola le cose insieme, se hai un paio di sottodirati diversi che hanno molti hardlink. (ovvero gruppi di gruppi duplici vanno insieme, ma l'output li confonde).

Un finale sort -k 3ordina le linee separatamente, non i gruppi di linee come un singolo record. La preelaborazione con qualcosa per trasformare una coppia di newline in un byte NUL e l'uso di GNU sort --zero-terminated -k 3potrebbe fare il trucco. trfunziona solo su singoli caratteri, non su 2-> 1 o 1-> 2 motivi. perllo farebbe (o semplicemente analizzerebbe e ordinerebbe all'interno di perl o awk). sedpotrebbe anche funzionare.


1
%Dè l'identificatore filesystem (è unica per l'avvio corrente mentre non filesystem sono umountndr), in modo che segue è ancora più generico: find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate. Funziona fino a quando nessuna data directory contiene un'altra directory a livello di filesystem, inoltre esamina tutto ciò che può essere hardlink (come dispositivi o softlink - sì, i softlink possono avere un conteggio di link maggiore di 1). Si noti che dev_te ino_tè lungo 64 bit oggi. Questo probabilmente durerà fino a quando avremo sistemi a 64 bit.
Tino,

@Tino: ottimo punto sull'uso di ! -type d, anziché -type f. Ho anche alcuni link simbolici sul mio filesystem dall'organizzazione di alcune raccolte di file. Aggiornato la mia risposta con la tua versione migliorata (ma ho inserito prima fs-id, quindi l'ordinamento almeno raggruppa per file system.)
Peter Cordes

3

Questo è in qualche modo un commento alla risposta e alla sceneggiatura di Torocoro-Macho, ma ovviamente non si adatta alla casella dei commenti.


Riscrivi la tua sceneggiatura con modi più semplici per trovare le informazioni e quindi molto meno invocazioni di 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

Ho cercato di mantenerlo il più simile possibile al tuo per un facile confronto.

Commenti su questo e il tuo script

  • Bisogna sempre evitare la $IFSmagia se un glob è sufficiente, poiché è inutilmente contorto, e i nomi dei file possono effettivamente contenere nuove righe (ma in pratica principalmente il primo motivo).

  • Dovresti evitare di analizzare manualmente lse tale output il più possibile, poiché prima o poi ti morderà. Ad esempio: nella prima awkriga, non riesci su tutti i nomi di file contenenti spazi.

  • printfsalverà spesso i problemi alla fine poiché è così robusto con la %ssintassi. Ti dà anche il pieno controllo sull'output ed è coerente su tutti i sistemi, a differenza echo.

  • stat può farti risparmiare molta logica in questo caso.

  • GNU find è potente.

  • Le tue heade le tue tailinvocazioni avrebbero potuto essere gestite direttamente awkcon, ad esempio, il exitcomando e / o selezionando la NRvariabile. Ciò salverebbe invocazioni di processo, che quasi sempre migliorano notevolmente le prestazioni negli script laboriosi.

  • I tuoi egreppotrebbero essere altrettanto grep.


xDEVICE = $ (stat -c% m "$ {xFILE}") non funziona su tutti i sistemi (ad esempio stat (GNU coreutils) 6.12). Se lo script restituisce "Item:?" all'inizio di ogni riga, quindi sostituisci questa riga offensiva con una riga più simile allo script originale, ma con xITEM rinominato in xFILE: xDEVICE = $ (df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien,

Se vuoi solo gruppi di hardlink, piuttosto che ripetere con ogni membro come "master", usa find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate. Questo è MOLTO più veloce, poiché attraversa la fs solo una volta. Per più FS contemporaneamente, è necessario aggiungere un prefisso ai numeri di inode con un ID FS. Forse confind -exec stat... -printf ...
Peter Cordes il

trasformò quell'idea in una risposta
Peter Cordes, il

2

Basato sullo findhardlinksscript (rinominato in hard-links), questo è ciò che ho modificato e fatto funzionare.

Produzione:

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

Ho pubblicato commenti su questo script come risposta separata.
Daniel Andersson,

1

Una soluzione GUI si avvicina molto alla tua domanda:

Non è possibile elencare i file con collegamento reale da "ls" perché, come hanno sottolineato i commentatori precedenti, i "nomi" dei file sono semplici alias degli stessi dati. Tuttavia, in realtà esiste uno strumento GUI che si avvicina molto a ciò che si desidera, ovvero visualizzare un elenco di percorsi di nomi di file che puntano agli stessi dati (come hardlink) in Linux, si chiama FSLint. L'opzione desiderata si trova in "Scontri per nome" -> deseleziona "casella $ PERCORSO" in Cerca (XX) -> e seleziona "Alias" dalla casella a discesa dopo "per ..." verso il centro.

FSLint è molto scarsamente documentato ma ho scoperto che assicurandomi che l'albero delle directory limitato in "Percorso di ricerca" con la casella di controllo selezionata per "Recurse?" e le opzioni di cui sopra, un elenco di dati hardlinked con percorsi e nomi che "puntano" agli stessi dati vengono prodotti dopo le ricerche del programma.


FSlint può essere trovato su pixelbeat.org/fslint
mklement0 l'

1

Puoi configurare lsper evidenziare i collegamenti fisici usando un 'alias', ma come detto prima non c'è modo di mostrare la 'fonte' del collegamento fisico, motivo per cui aggiungo .hardlinkper aiutarlo.

evidenziare hardlink

Aggiungi quanto segue da qualche parte nel tuo .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.