Come si fa a vedere l'effettivo collegamento rigido tramite ls?
Eseguo
ln /a/A /b/B
vorrei vedere nella cartella a
dove punta il file A da ls
.
Eseguo
ln /a/A /b/B
vorrei vedere nella cartella a
dove punta il file A da ls
.
Potete trovare il numero di inode per il vostro file con
ls -i
e
ls -l
mostra il numero di riferimenti (numero di hardlink a un particolare inode)
dopo aver trovato il numero di inode, potete cercare tutti i file con lo stesso inode:
find . -inum NUM
mostrerà i nomi dei file per inode NUM nella directory corrente (.)
Non c'è una risposta ben definita alla tua domanda. A differenza dei symlink, gli hardlink sono indistinguibili dal “file originale”.
Le voci della directory consistono in un nome di file e un puntatore a un inode. L'inode a sua volta contiene i metadati del file e (puntatori al) contenuto effettivo del file). La creazione di un hard link crea un altro nome di file + riferimento allo stesso inode. Questi riferimenti sono unidirezionali (nei filesystem tipici, almeno) – l'inode mantiene solo un conteggio dei riferimenti. Non c'è un modo intrinseco per scoprire quale sia il nome del 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 e i dati collegati vengono cancellati solo se il numero di riferimenti all'inode scende a 0.
L'unico modo per trovare gli altri riferimenti a un dato inode è di cercare esaustivamente nel file system controllando quali file fanno riferimento all'inode in questione. Puoi usare ‘test A -ef B’ dalla shell per eseguire questo controllo.
ls -l
La prima colonna rappresenterà i permessi. La seconda colonna sarà il numero di sotto-voci (per le directory) o il numero di percorsi per gli stessi dati (hard link, incluso il file originale) al file. Es:
-rw-r--r--@ 2 [username] [group] [timestamp] HardLink
-rw-r--r--@ 2 [username] [group] [timestamp] Original
^ Number of hard links to the data
Che ne dite del seguente, più semplice? (Quest'ultima potrebbe sostituire i lunghi script di cui sopra!)
Se hai un file specifico <THEFILENAME>
e vuoi conoscere tutti i suoi hardlink sparsi nella directory <TARGETDIR>
, (che può anche essere l'intero filesystem indicato da /
)
find <TARGETDIR> -type f -samefile <THEFILENAME>
Estendendo la logica, se vuoi conoscere tutti i file nella <SOURCEDIR>
che hanno più hardlink 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 {} \;
Ci sono molte risposte con 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 per -samefile
per OGNI file multiply-linked. Questo è folle; tutto ciò di cui avete bisogno è ordinare in base al numero di inode e stampare i duplicati.
Con un solo passaggio sul filesystem per trovare e raggruppare tutti i set di file hardlinked
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
: limitare a un solo filesystem. Non strettamente necessario dato che stampiamo anche l'FS-id a uniq su ! -type d
rifiuta le directory: le voci .
e ..
significano che sono sempre collegate. -links +1
: conteggio dei link strettamente > 1
-printf ...
stampa FS-id, numero di inode, e percorso. (Con il padding a larghezze di colonna fisse che possiamo comunicare a uniq
.) sort -n | uniq ...
ordinamento numerico e univoco sulle prime 42 colonne, separando i gruppi con una linea vuota Usare ! -type d -links +1
significa che l'input di sort è grande solo quanto l'output finale di uniq, quindi non stiamo facendo un'enorme quantità di ordinamento di stringhe. A meno che non lo eseguiate su una sottodirectory che contiene solo uno di una serie di hardlink. Comunque, questo userà molto meno tempo di CPU per attraversare nuovamente il filesystem rispetto a qualsiasi altra soluzione postata.
esempio di output:
...
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?: disimpegna l'output con awk
o cut
. uniq
ha un supporto molto limitato per la selezione dei campi, quindi imbottisco l'output di find e uso la larghezza fissa. 20 caratteri sono sufficienti per il massimo numero possibile di inode o di dispositivo (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 grandi filesystem XFS possono avere numeri di inode di >32bit anche se non hanno miliardi di file. Altri filesystem potrebbero avere numeri di inode di 20 cifre anche se non sono giganteschi.
TODO: ordinare i gruppi di duplicati per percorso. Averli ordinati per punto di montaggio e poi per numero di inode confonde le cose, se si hanno un paio di sottodirectory diversi che hanno molti hardlink. (cioè i gruppi di dup-groups vanno insieme, ma l'output li mischia).
Un finale sort -k 3
ordinerebbe le linee separatamente, non gruppi di linee come un singolo record. La pre-elaborazione con qualcosa che trasformi una coppia di newline in un byte NUL, e l'uso di GNU sort --zero-terminated -k 3
potrebbe fare il trucco. tr
opera solo su caratteri singoli, non su schemi 2->1 o 1->2, però. perl
lo farebbe (o semplicemente analizzare e ordinare in perl o awk). Anche sed
potrebbe funzionare.
Questo è un po’ un commento alla risposta e allo script di Torocoro-Macho, ma ovviamente non ci sta nella casella dei commenti._
Riscrivere il tuo script con modi più diretti per trovare le informazioni, e quindi molte 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ù possibile simile al tuo per un facile confronto.
Si dovrebbe sempre evitare la magia $IFS
se basta un glob, poiché è inutilmente contorto, e i nomi dei file possono effettivamente contenere newline (ma in pratica soprattutto la prima ragione).
Dovresti evitare il più possibile di analizzare manualmente l'output di ls
e simili, dato che prima o poi ti morderà. Per esempio: nella tua prima linea awk
, fallisci su tutti i nomi di file che contengono spazi.
printf
vi risparmierà spesso problemi alla fine, poiché è così robusto con la sintassi %s
. Vi dà anche il pieno controllo sull'output, ed è coerente su tutti i sistemi, a differenza di echo
.
stat
può farvi risparmiare un sacco di logica in questo caso.
GNU find
è potente.
Le tue invocazioni head
e tail
avrebbero potuto essere gestite direttamente in awk
con ad esempio il comando exit
e/o selezionando sulla variabile NR
. Questo risparmierebbe le invocazioni di processo, il che migliora quasi sempre le prestazioni negli script che lavorano duramente.
I tuoi egrep
potrebbero benissimo essere solo grep
.
Sulla base dello script findhardlinks
(rinominato in hard-links
), questo è ciò che ho rifatto e fatto funzionare.
Uscita:
# ./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 gli effettivi file hardlinked da “ls” perché, come hanno sottolineato i commentatori precedenti, i “nomi” dei file sono semplici alias degli stessi dati. Tuttavia, c'è uno strumento GUI che si avvicina molto a quello che vuoi, cioè visualizzare un elenco di percorsi di nomi di file che puntano agli stessi dati (come hardlink) sotto linux, si chiama FSLint. L'opzione che vuoi è sotto “Name clashes” -> deseleziona “checkbox $PATH” in Search (XX) -> e seleziona “Alias” dalla casella a discesa dopo “for…” verso il centro in alto.
FSLint è molto poco documentato, ma ho scoperto che assicurandomi che l'albero delle directory limitato sotto “Percorso di ricerca” con la casella di controllo selezionata per “Ricorri?” e le suddette opzioni, un elenco di dati hardlinked con percorsi e nomi che “puntano” agli stessi dati sono prodotti dopo le ricerche del programma.
Puoi configurare ls
per evidenziare gli hardlink usando un ‘alias’, ma come detto prima non c'è modo di mostrare la ‘fonte’ dell'hardlink che è il motivo per cui aggiungo .hardlink
per aiutare in questo.
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'