io corro
ln /a/A /b/B
Vorrei vedere nella cartella in a
cui il file A indica ls
.
io corro
ln /a/A /b/B
Vorrei vedere nella cartella in a
cui il file A indica ls
.
Risposte:
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 (.)
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.
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
. ./findhardlinks.bash
mentre mi trovo nella Zsh di OS X. La mia finestra corrente in Schermo si chiude.
INUM=$(stat -c %i $1)
. Inoltre NUM_LINKS=$(stat -c %h $1)
. Vedi man stat
per altre variabili di formato che puoi usare.
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
inode
che a sua volta punta al contenuto del disco.
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 {} \;
-type f
perché il file può essere anche una directory.
.
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
.
O(n^2)
, e viene eseguita find
una volta per ogni membro di una serie di file con collegamenti fissi. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate
funzionerebbe, (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)
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 -samefile
per 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 d
rifiuta 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 vuotaUsare ! -type d -links +1
significa 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 awk
o cut
. uniq
ha 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 3
ordina 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 3
potrebbe fare il trucco. tr
funziona solo su singoli caratteri, non su 2-> 1 o 1-> 2 motivi. perl
lo farebbe (o semplicemente analizzerebbe e ordinerebbe all'interno di perl o awk). sed
potrebbe anche funzionare.
%D
è l'identificatore filesystem (è unica per l'avvio corrente mentre non filesystem sono umount
ndr), 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_t
e ino_t
è lungo 64 bit oggi. Questo probabilmente durerà fino a quando avremo sistemi a 64 bit.
! -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.)
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.
Bisogna sempre evitare la $IFS
magia 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 ls
e tale output il più possibile, poiché prima o poi ti morderà. Ad esempio: nella prima awk
riga, non riesci su tutti i nomi di file contenenti spazi.
printf
salverà spesso i problemi alla fine poiché è così robusto con la %s
sintassi. 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 head
e le tue tail
invocazioni avrebbero potuto essere gestite direttamente awk
con, ad esempio, il exit
comando e / o selezionando la NR
variabile. Ciò salverebbe invocazioni di processo, che quasi sempre migliorano notevolmente le prestazioni negli script laboriosi.
I tuoi egrep
potrebbero essere altrettanto grep
.
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 ...
Basato sullo findhardlinks
script (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 "";
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.
Puoi configurare ls
per 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 .hardlink
per aiutarlo.
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'
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"ls
nell'output).