ich renne
ln /a/A /b/B
Ich würde gerne den Ordner sehen, auf den a
die Datei A zeigt ls
.
ich renne
ln /a/A /b/B
Ich würde gerne den Ordner sehen, auf den a
die Datei A zeigt ls
.
Antworten:
Sie finden die Inode-Nummer für Ihre Datei mit
ls -i
und
ls -l
Zeigt die Anzahl der Referenzen an (Anzahl der Hardlinks zu einem bestimmten Inode)
Nachdem Sie die Inode-Nummer gefunden haben, können Sie nach allen Dateien mit demselben Inode suchen:
find . -inum NUM
Zeigt die Dateinamen für Inode NUM im aktuellen Verzeichnis (.) an.
Es gibt keine eindeutige Antwort auf Ihre Frage. Im Gegensatz zu Symlinks sind Hardlinks nicht von der "Originaldatei" zu unterscheiden.
Verzeichniseinträge bestehen aus einem Dateinamen und einem Zeiger auf eine Inode. Der Inode wiederum enthält die Dateimetadaten und (Verweise auf) den tatsächlichen Dateiinhalt. Durch das Erstellen eines festen Links wird ein anderer Dateiname + Verweis auf denselben Inode erstellt. Diese Referenzen sind unidirektional (zumindest in typischen Dateisystemen) - der Inode behält nur eine Referenzanzahl bei. Es gibt keinen eigentlichen Weg, um herauszufinden, welcher der "ursprüngliche" Dateiname ist.
Dies ist übrigens der Grund, warum der Systemaufruf zum "Löschen" einer Datei aufgerufen wird unlink
. Es wird nur ein Hardlink entfernt. Der Inode und die angehängten Daten werden nur gelöscht, wenn der Referenzzähler des Inodes auf 0 fällt.
Der einzige Weg, die anderen Verweise auf einen bestimmten Inode zu finden, besteht darin, das Dateisystem gründlich zu durchsuchen und zu prüfen, welche Dateien auf den betreffenden Inode verweisen. Sie können 'test A -ef B' in der Shell verwenden, um diese Prüfung durchzuführen.
UNIX hat harte Links und symbolische Links (hergestellt mit "ln"
und "ln -s"
jeweils). Symbolische Links sind einfach eine Datei, die den tatsächlichen Pfad zu einer anderen Datei enthält und Dateisysteme überqueren kann.
Harte Links gibt es schon seit den Anfängen von UNIX (an die ich mich sowieso erinnern kann und die schon eine ganze Weile zurückreichen). Dies sind zwei Verzeichniseinträge, die auf genau dieselben zugrunde liegenden Daten verweisen . Die Daten in einer Datei werden durch ihre angegeben inode
. Jede Datei in einem Dateisystem verweist auf einen Inode, aber es ist nicht erforderlich, dass jede Datei auf einen eindeutigen Inode verweist - daher kommen harte Links.
Da Inodes nur für ein bestimmtes Dateisystem eindeutig sind, müssen sich harte Links (im Gegensatz zu symbolischen Links) auf demselben Dateisystem befinden. Beachten Sie, dass es im Gegensatz zu symbolischen Links keine privilegierten Dateien gibt - sie sind alle gleich. Der Datenbereich wird erst freigegeben, wenn alle Dateien gelöscht wurden, die diesen Inode verwenden (und alle Prozesse schließen ihn ebenfalls, aber das ist ein anderes Problem).
Sie können den "ls -i"
Befehl verwenden, um den Inode einer bestimmten Datei abzurufen. Sie können dann den "find <filesystemroot> -inum <inode>"
Befehl verwenden, um alle Dateien im Dateisystem mit dem angegebenen Inode zu finden.
Hier ist ein Skript, das genau das macht. Sie rufen es auf mit:
findhardlinks ~/jquery.js
und es werden alle Dateien auf diesem Dateisystem gefunden, die feste Links für diese Datei sind:
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
Hier ist das Drehbuch.
#!/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
starte es, indem ich mich in Zsh von OS X befinde. Mein aktuelles Fenster auf dem Bildschirm wird geschlossen.
INUM=$(stat -c %i $1)
. Auch NUM_LINKS=$(stat -c %h $1)
. Siehe man stat
für weitere Formatvariablen Sie verwenden können.
ls -l
Die erste Spalte enthält die Berechtigungen. In der zweiten Spalte wird die Anzahl der Unterelemente (für Verzeichnisse) oder die Anzahl der Pfade zu denselben Daten (Hardlinks, einschließlich der Originaldatei) zur Datei angegeben. Z.B:
-rw-r--r--@ 2 [username] [group] [timestamp] HardLink
-rw-r--r--@ 2 [username] [group] [timestamp] Original
^ Number of hard links to the data
inode
was wiederum auf den Disc-Inhalt verweist.
Wie wäre es mit dem folgenden einfacheren? (Letzteres könnte die langen Skripte oben ersetzen!)
Wenn Sie eine bestimmte Datei haben <THEFILENAME>
und wissen möchten, welche Hardlinks über das Verzeichnis verteilt <TARGETDIR>
sind (dies kann sogar das gesamte Dateisystem sein, das mit bezeichnet wird /
)
find <TARGETDIR> -type f -samefile <THEFILENAME>
Erweitern Sie die Logik, wenn Sie alle Dateien in den <SOURCEDIR>
mit mehreren Hardlinks verteilten wissen möchten <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
weil die Datei auch ein Verzeichnis sein kann.
.
und ..
Einträge in Verzeichnissen sind Hardlinks. Sie können anhand der Linkanzahl von feststellen, wie viele Unterverzeichnisse sich in einem Verzeichnis befinden .
. Dies ist ohnehin umstritten, da find -samefile .
immer noch keine subdir/..
Ausgabe gedruckt wird. find
(zumindest die GNU-Version) scheint hartkodiert zu sein ..
, auch mit zu ignorieren -noleaf
.
O(n^2)
, alle Links zu finden, ist und wird find
einmal für jedes Mitglied eines Satzes fest verknüpfter Dateien ausgeführt. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate
würde funktionieren (16 ist nicht breit genug für eine Dezimaldarstellung von 2 ^ 63-1, wenn Ihr XFS-Dateisystem also groß genug ist, um so hohe Inode-Nummern zu haben,
Es gibt viele Antworten mit Skripten, um alle Hardlinks in einem Dateisystem zu finden. Die meisten von ihnen machen dumme Dinge wie das Ausführen von find, um das gesamte Dateisystem -samefile
nach JEDER mehrfach verknüpften Datei zu durchsuchen. Das ist verrückt; Alles, was Sie brauchen, ist nach Inode-Nummer zu sortieren und Duplikate auszudrucken.
Mit nur einem Durchlauf über das Dateisystem finden und gruppieren Sie alle Sätze von fest verknüpften Dateien
find dirs -xdev \! -type d -links +1 -printf '%20D %20i %p\n' |
sort -n | uniq -w 42 --all-repeated=separate
Dies ist viel schneller als die anderen Antworten zum Auffinden mehrerer Sätze von fest verknüpften Dateien.
find /foo -samefile /bar
eignet sich hervorragend für nur eine Datei.
-xdev
: Beschränkung auf ein Dateisystem. Nicht unbedingt erforderlich, da wir die FS-ID auch ausdrucken, um sie einzuschalten! -type d
Verzeichnisse ablehnen: Die Einträge .
und ..
bedeuten, dass sie immer verknüpft sind.-links +1
: Link zählen streng > 1
-printf ...
Gibt die FS-ID, die Inode-Nummer und den Pfad aus. (Mit Auffüllung auf feste Spaltenbreiten, über die wir etwas sagen können uniq
.)sort -n | uniq ...
Numerische Sortierung und Eindeutigkeit in den ersten 42 Spalten, wobei die Gruppen durch eine Leerzeile getrennt werdenUsing ! -type d -links +1
bedeutet, dass die Eingabe von sort nur so groß ist wie die endgültige Ausgabe von uniq, sodass wir nicht viel nach Zeichenfolgen sortieren müssen. Es sei denn, Sie führen es in einem Unterverzeichnis aus, das nur einen von mehreren Hardlinks enthält. Auf jeden Fall wird dies VIEL weniger CPU-Zeit in Anspruch nehmen, um das Dateisystem erneut zu durchlaufen als jede andere veröffentlichte Lösung.
Beispielausgabe:
...
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 ?: Klappen Sie die Ausgabe mit awk
oder auf cut
. uniq
Da die Unterstützung für die Feldauswahl sehr begrenzt ist, fülle ich die Suchausgabe auf und verwende eine feste Breite. 20 Zeichen sind breit genug für die maximal mögliche Inode- oder Gerätenummer (2 ^ 64-1 = 18446744073709551615). XFS wählt Inode-Nummern basierend auf dem Speicherort, an dem sie zugewiesen sind, und nicht zusammenhängend von 0, sodass große XFS-Dateisysteme> 32-Bit-Inode-Nummern haben können, auch wenn sie nicht über Milliarden von Dateien verfügen. Andere Dateisysteme haben möglicherweise 20-stellige Inode-Nummern, auch wenn sie nicht gigantisch sind.
TODO: Sortiert Gruppen von Duplikaten nach Pfad. Wenn Sie sie nach Einhängepunkt und dann nach Inode-Nummer sortiert haben, werden die Dinge gemischt, wenn Sie ein paar verschiedene Unterverzeichnisse haben, die viele Hardlinks haben. (dh Gruppen von Dup-Gruppen gehören zusammen, aber die Ausgabe mischt sie).
Ein Finale sort -k 3
würde die Zeilen separat sortieren, nicht die Zeilengruppen als einen einzigen Datensatz. Die Vorverarbeitung mit etwas, um ein Paar Zeilenumbrüche in ein NUL-Byte umzuwandeln, und die Verwendung von GNU sort --zero-terminated -k 3
könnte den Trick tun. tr
Funktioniert jedoch nur mit einzelnen Zeichen, nicht mit Mustern von 2> 1 oder 1> 2. perl
würde es tun (oder einfach in Perl oder awk analysieren und sortieren). sed
könnte auch funktionieren.
%D
ist das Dateisystem - ID (es einzigartig für den aktuellen Start ist , während keine Dateisysteme sind umount
ed), so finden Sie noch mehr generic: find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate
. Dies funktioniert, solange kein bestimmtes Verzeichnis ein anderes Verzeichnis auf Dateisystemebene enthält. Außerdem werden alle Elemente angezeigt, die per Hardlink verbunden werden können (z. B. Geräte oder Softlinks - ja, bei Softlinks kann die Anzahl der Links größer als 1 sein). Beachten Sie, dass dev_t
und ino_t
ist heute 64 Bit lang. Dies wird wahrscheinlich so lange anhalten, wie wir 64-Bit-Systeme haben.
! -type d
anstelle von -type f
. Ich habe sogar einige fest verknüpfte Symlinks in meinem Dateisystem, weil ich einige Dateisammlungen organisiert habe. Meine Antwort wurde mit Ihrer verbesserten Version aktualisiert (aber ich habe die fs-id an die erste Stelle gesetzt, damit die Sortierreihenfolge mindestens nach Dateisystem gruppiert wird.)
Dies ist eine Art Kommentar zu Torocoro-Machos eigener Antwort und seinem Skript, passt aber offensichtlich nicht in das Kommentarfeld.
Schreiben Sie Ihr Skript mit einfacheren Methoden um, um die Informationen zu finden, und verarbeiten Sie damit weniger Aufrufe.
#!/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
Ich habe versucht, es für einen einfachen Vergleich so ähnlich wie möglich zu halten.
Man sollte die $IFS
Magie immer vermeiden, wenn ein Glob ausreicht, da er unnötig verschlungen ist und Dateinamen tatsächlich Zeilenumbrüche enthalten können (in der Praxis jedoch meistens der erste Grund).
Sie sollten manuelles Parsen ls
und solche Ausgaben so weit wie möglich vermeiden , da es Sie früher oder später beißen wird. Beispiel: In Ihrer ersten awk
Zeile schlagen Sie alle Dateinamen fehl, die Leerzeichen enthalten.
printf
wird am Ende oft Probleme ersparen, da es mit der %s
Syntax so robust ist . Außerdem haben Sie die volle Kontrolle über die Ausgabe und sind im Gegensatz zu allen anderen Systemen konsistent echo
.
stat
kann Ihnen in diesem Fall viel Logik ersparen.
GNU find
ist mächtig.
Ihre head
und tail
Aufrufe könnten direkt awk
mit zB dem exit
Befehl und / oder der Auswahl auf der NR
Variablen behandelt worden sein. Dies würde Prozessaufrufe einsparen, die die Leistung in hart arbeitenden Skripten fast immer stark beeinträchtigen.
Dein egrep
s könnte genauso gut sein grep
.
find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate
. Dies ist VIEL schneller, da es die fs nur einmal durchläuft. Für mehrere FSes gleichzeitig müssen Sie den Inode-Nummern eine FS-ID voranstellen. Vielleicht mitfind -exec stat... -printf ...
Basierend auf dem findhardlinks
Skript (umbenannt in hard-links
) habe ich es überarbeitet und zum Laufen gebracht.
Ausgabe:
# ./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 "";
Eine GUI-Lösung kommt Ihrer Frage sehr nahe:
Sie können die tatsächlichen fest verknüpften Dateien von "ls" nicht auflisten, da die "Dateinamen", wie bereits von früheren Kommentatoren erwähnt, nur Aliase für dieselben Daten sind. Tatsächlich gibt es jedoch ein GUI-Tool, das dem, was Sie wollen, sehr nahe kommt und eine Pfadauflistung von Dateinamen anzeigt, die unter Linux auf dieselben Daten (als Hardlinks) verweisen. Es heißt FSLint. Die gewünschte Option befindet sich unter "Namenskonflikte" -> Deaktivieren Sie das Kontrollkästchen "$ PATH" in der Suche (XX) -> und wählen Sie "Aliase" in der Dropdown-Box nach "für ..." in der oberen Mitte aus.
FSLint ist sehr schlecht dokumentiert, aber ich habe festgestellt, dass der eingeschränkte Verzeichnisbaum unter "Suchpfad" mit dem Kontrollkästchen "Recurse?" und die zuvor genannten Optionen, eine Auflistung von fest verbundenen Daten mit Pfaden und Namen, die auf die gleichen Daten "verweisen", werden nach der Programmsuche erzeugt.
Sie können festlegen ls
, dass Hardlinks mit einem "Alias" hervorgehoben werden. Wie bereits erwähnt, kann die "Quelle" des Hardlinks jedoch nicht angezeigt werden. Aus diesem Grund füge ich .hardlink
an, um Ihnen dabei zu helfen.
Fügen Sie Folgendes irgendwo in Ihre Liste ein .bashrc
alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
link(2)
Systemaufruf hat es keinen Sinn, in welcher Form eine das Original und eine die Verknüpfung ist. Wie die Antworten zeigen, ist der einzige Weg, alle Links zu finden, derfind / -samefile /a/A
. Da ein Verzeichniseintrag für einen Inode andere Verzeichniseinträge für denselben Inode nicht "kennt". Alles, was sie tun, ist die Inode neu zu zählen, damit sie gelöscht werden kann, wenn der Nachname dafür istunlink(2)ed
. (Dies ist die "Linkanzahl" in derls
Ausgabe).