it-swarm-eu.dev

Come puoi vedere il vero e proprio hard link di ls?

Io corro

ln /a/A /b/B

Mi piacerebbe vedere nella cartella a dove il file A punta da ls.

Puoi trovare il numero di inode per il tuo file con

ls -i

e

ls -l

mostra il conteggio dei riferimenti (numero di collegamenti fisici a un particolare inode)

dopo aver trovato il numero di inode, puoi cercare tutti i file con lo stesso inode:

find . -inum NUM

mostrerà il nome del file per l'inode NUM nella directory corrente (.)

162
zzr

Non c'è una risposta ben definita alla tua domanda. A differenza dei collegamenti simbolici, i collegamenti fisici 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 fisico crea un altro nome file + riferimento allo stesso inode. Questi riferimenti sono unidirezionali (almeno nei filesystem tipici): l'inode conserva solo un conteggio dei riferimenti. Non esiste un modo intrinseco per scoprire quale sia il nome file "originale".

A proposito, questo è il motivo per cui la chiamata di sistema per "cancellare" un file si chiama unlink. Rimuove solo un hardlink. L'inode i dati allegati vengono eliminati solo se il conteggio dei riferimenti inode scende a 0.

L'unico modo per trovare gli altri riferimenti a un determinato inode è cercare esaurientemente sul file system controllando quali file si riferiscono all'inode in questione. È possibile utilizzare 'test A -ef B' da Shell per eseguire questo controllo.

59

UNIX ha collegamenti fisici e collegamenti simbolici (realizzati rispettivamente con "ln" e "ln -s"). I link simbolici sono semplicemente un file che contiene il vero percorso di un altro file e può attraversare i file system.

Gli hard link sono in circolazione fin dai primi giorni di UNIX (che posso ricordare comunque, e che sta tornando un po 'di tempo). Sono due voci di directory che fanno riferimento a exact same dei dati 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 punti a un inode univoco, ovvero da dove provengono i collegamenti fisici.

Poiché gli inode sono unici solo per un determinato file system, esiste una limitazione che i collegamenti fisici devono essere sullo stesso file system (diversamente dai collegamenti simbolici). Nota che, a differenza dei link simbolici, non esiste un file privilegiato - sono tutti uguali. L'area dati verrà rilasciata solo quando all i file che utilizzano quell'inode vengono eliminati (e tutti i processi lo chiudono, ma si tratta di un problema diverso).

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

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:

[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

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
33
user53528
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 fisici, incluso il file originale) al file. Per esempio:

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

Che ne dici del seguente più semplice? (Latter potrebbe sostituire i lunghi script sopra!)

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

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

Estendendo la logica, se vuoi conoscere tutti i file nel <SOURCEDIR> con più hard link sparsi su <TARGETDIR>:

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

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

find directories.. -xdev ! -type d -links +1 -printf '%20D %20i %p\n' | sort -n | uniq -w 42 --all-repeated=separate (Grazie a @Tino per aver ottimizzato il mio comando originale per supportare un FS-id (%D), e per gestire tutti i tipi di file non di directory, non solo i file normali.Questo troverà i link simbolici, pipe, ecc.

L'uso di ! -type d -links +1 significa che l'input di sort è grande quanto l'output finale di uniq. A meno che non lo si esegua su una sottodirectory che contiene solo uno di un set di collegamenti fissi. Ad ogni modo, ciò richiederà un tempo di CPU MOLTO inferiore che ri-attraversa il filesystem rispetto a qualsiasi altra soluzione pubblicata.

uscita di esempio:

...
            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?: Disattiva l'output. uniq ha un supporto per la selezione dei campi molto limitato, quindi inserisco l'output di ricerca e uso la larghezza fissa. 20 caratteri è sufficientemente ampio per il massimo numero di inode o dispositivo possibile (2 ^ 64-1 = 18446744073709551615). XFS sceglie i numeri di inode in base a dove sono allocati sul disco, non in modo contiguo da 0, quindi i file system XFS di grandi dimensioni possono avere numeri di inode> 32bit anche se non hanno miliardi di file. Altri file system potrebbero avere numeri di inode a 20 cifre anche se non sono giganteschi.

TODO: ordina i gruppi di duplicati per percorso. Dopo averli ordinati per punto di mount, il numero di inode mescola le cose insieme, se si dispone di un paio di sottodir diversi che hanno un sacco di hardlinks. (cioè gruppi di gruppi dup vanno insieme, ma l'output li miscela).

Un sort -k 3 finale ordina le righe separatamente, non i gruppi di righe come un singolo record. Pre-elaborazione con qualcosa per trasformare una coppia di newline in un byte NUL e usare GNU sort --zero-terminated -k 3 potrebbe fare il trucco. tr funziona solo su caratteri singoli, non su modelli 2-> 1 o 1-> 2. Perl lo farebbe (o semplicemente analizzerà e ordinerà in Perl o awk). sed potrebbe anche funzionare.

4
Peter Cordes

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


Riscrivi il tuo script con metodi 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 script e il tuo

  • Si dovrebbe sempre evitare la magia $IFS se un glob è sufficiente, poiché è inutilmente complicato, e i nomi di file possono effettivamente contenere newline (ma in pratica per lo più il primo motivo).

  • Dovresti evitare di analizzare manualmente lse tale output il più possibile, dal momento che prima o poi ti morderà. Ad esempio: nella prima riga awkname__, non si riesce su tutti i nomi di file che contengono spazi.

  • printfspesso salverà i problemi alla fine poiché è così robusto con la sintassi %s. Ti dà anche il pieno controllo sull'output, ed è coerente su all systems, a differenza di echoname__.

  • statpuò farti risparmiare molta logica in questo caso.

  • GNU find è potente.

  • Le tue invocazioni heade tailpotrebbero essere state gestite direttamente in awkcon ad es. il comando exite/o la selezione nella variabile NRname__. Ciò farebbe risparmiare le invocazioni dei processi, che quasi sempre migliorano le prestazioni in modo severo negli script laboriosi.

  • Il tuo egrepname__s potrebbe anche essere solo grepname__.

3
Daniel Andersson

Basato sullo script findhardlinks (rinominato in hard-links), questo è ciò che ho refactored 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 "";
2
Torocoro-Macho

Una soluzione GUI si avvicina molto alla tua domanda:

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

FSLint è molto scarsamente documentato, ma ho trovato che assicurarmi 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.

1
Charles

È possibile configurare ls per evidenziare gli hardlink usando un 'alias', ma come detto prima non c'è modo di mostrare il 'source' del collegamento fisico, motivo per cui accludo .hardlink per aiutare in questo.

 highlight hardlinks

Aggiungi il seguente da qualche parte nel tuo .bashrc

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