Wie können Sie den tatsächlichen Hardlink von ls sehen?

97

ich renne

ln /a/A /b/B

Ich würde gerne den Ordner sehen, auf den adie Datei A zeigt ls.

Léo Léopold Hertz 준영
quelle
1
Harte Links sind keine Zeiger, Symlinks sind. Es sind mehrere Namen für dieselbe Datei (Inode). Nach einem 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, der find / -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 ist unlink(2)ed. (Dies ist die "Linkanzahl" in der lsAusgabe).
Peter Cordes
@PeterCordes: Ist der Refcount tatsächlich im Hardlink-Eintrag gespeichert? Das ist, was Ihre Formulierung impliziert ("Alles, was sie tun, ist die Inode neu zu zählen ..."). Aber das würde keinen Sinn ergeben, wenn die Links nichts voneinander wissen, da alle anderen, wenn sie aktualisiert werden, es irgendwie tun müssten bleibe auf dem Laufenden. Oder ist der Nachzähler in der Inode selbst gespeichert? (Verzeih mir, wenn es eine blöde Frage ist, ich betrachte mich als Neuling und lerne noch).
Loneboat
1
Die Nachzählung wird in der Inode gespeichert, wie Sie eventuell anhand der anderen Fakten herausgefunden haben. :) Verzeichniseinträge sind benannte Zeiger auf Inodes. Wir nennen es "Hardlinking", wenn Sie mehrere Namen haben, die auf den gleichen Inode verweisen.
Peter Cordes

Antworten:

171

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.

zzr
quelle
46
du könntest einfach laufen und finden. -samefile Dateiname
BeowulfNode42
1
@ BeowulfNode42 Dieser Befehl ist großartig, benötigt aber mindestens den freigegebenen Stammordner derselben Dateien.
Itachi
1
Diese Antwort gibt ein pragmatisches "dies tun", aber ich bin fest davon überzeugt, dass @LaurenceGonsalves die Fragen "wie" und / oder "warum" beantwortet.
Trevor Boyd Smith
65

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.

Laurence Gonsalves
quelle
35
Das bedeutet, dass es keinen festen Link zu einer anderen Datei gibt , da die Originaldatei auch ein fester Link ist. Feste Links verweisen auf einen Speicherort auf der Festplatte .
Jtbandes
12
@jtbandes: Feste Links verweisen auf einen Inode, der auf die tatsächlichen Daten verweist.
Dash17291
33

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

quelle
@pax: Es scheint einen Fehler im Skript zu geben. Ich . ./findhardlinks.bashstarte es, indem ich mich in Zsh von OS X befinde. Mein aktuelles Fenster auf dem Bildschirm wird geschlossen.
4
@Masi Das Problem ist Ihre Initiale. (Wie der Quellbefehl). Das bewirkt, dass der Befehl exit 1 Ihre Shell beendet. Benutze chmod a + x findhardlinks.bash und führe es dann mit ./findhardlinks.bash aus oder benutze bash findhardlinks.bash
njsf
Bitte beachten Sie meine Antwort auf Ihre Antwort unter superuser.com/questions/12972/to-see-hardlinks-by-ls/…
Léo Léopold Hertz 준영
3
Um dies programmatisch zu tun, ist es wahrscheinlich belastbarer , wenn Sie diese verwenden , anstatt: INUM=$(stat -c %i $1). Auch NUM_LINKS=$(stat -c %h $1). Siehe man statfür weitere Formatvariablen Sie verwenden können.
Joe
Mit Abstand die beste Antwort. Ein dickes Lob.
MariusMatutiae
24
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
Augenlosigkeit
quelle
2
Hilfreich beim Bestimmen, ob eine bestimmte Datei [andere] feste Links hat, aber nicht, wo sie sind.
mklement0
Außerdem gibt es keinen technischen Unterschied zwischen einem Hardlink und einer Originaldatei. Sie sind beide insofern identisch, als sie einfach auf das zeigen, inodewas wiederum auf den Disc-Inhalt verweist.
Guyarad
13

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 {} \; 
Liebt Wahrscheinlichkeit
quelle
Das ist für mich die beste Antwort! aber ich würde nicht verwenden, -type fweil die Datei auch ein Verzeichnis sein kann.
Silvio
3
@silvio: Sie können nur feste Links zu Dateien erstellen , keine Verzeichnisse.
mklement0
@ mklement0: Du hast recht!
Silvio
Die .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.
Peter Cordes
Diese Idee O(n^2), alle Links zu finden, ist und wird findeinmal für jedes Mitglied eines Satzes fest verknüpfter Dateien ausgeführt. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separatewü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,
Peter Cordes
5

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 -samefilenach 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 /bareignet 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 dVerzeichnisse 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 werden

Using ! -type d -links +1bedeutet, 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 awkoder auf cut. uniqDa 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 3wü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 3könnte den Trick tun. trFunktioniert jedoch nur mit einzelnen Zeichen, nicht mit Mustern von 2> 1 oder 1> 2. perlwürde es tun (oder einfach in Perl oder awk analysieren und sortieren). sedkönnte auch funktionieren.

Peter Cordes
quelle
1
%Dist das Dateisystem - ID (es einzigartig für den aktuellen Start ist , während keine Dateisysteme sind umounted), 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_tund ino_tist heute 64 Bit lang. Dies wird wahrscheinlich so lange anhalten, wie wir 64-Bit-Systeme haben.
Tino
@Tino: Toller Punkt über die Verwendung von ! -type danstelle 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.)
Peter Cordes
3

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.

Kommentare zu diesem und Ihrem Skript

  • Man sollte die $IFSMagie 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 lsund solche Ausgaben so weit wie möglich vermeiden , da es Sie früher oder später beißen wird. Beispiel: In Ihrer ersten awkZeile schlagen Sie alle Dateinamen fehl, die Leerzeichen enthalten.

  • printfwird am Ende oft Probleme ersparen, da es mit der %sSyntax 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 headund tailAufrufe könnten direkt awkmit zB dem exitBefehl und / oder der Auswahl auf der NRVariablen behandelt worden sein. Dies würde Prozessaufrufe einsparen, die die Leistung in hart arbeitenden Skripten fast immer stark beeinträchtigen.

  • Dein egreps könnte genauso gut sein grep.

Daniel Andersson
quelle
xDEVICE = $ (stat -c% m "$ {xFILE}") funktioniert nicht auf allen Systemen (zum Beispiel: stat (GNU coreutils) 6.12). Wenn das Skript "Item:?" Ersetzen Sie diese fehlerhafte Zeile am Anfang jeder Zeile durch eine Zeile, die dem ursprünglichen Skript ähnelt, jedoch in xFILE umbenannt wurde: xDEVICE = $ (df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien
Wenn Sie nur Gruppen von Hardlinks wünschen, anstatt sie mit jedem Mitglied als "Master" zu wiederholen, verwenden Sie 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 ...
Peter Cordes
machte aus dieser Idee eine Antwort
Peter Cordes
2

Basierend auf dem findhardlinksSkript (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 "";
Torocoro-Macho
quelle
Ich habe Kommentare zu diesem Skript als separate Antwort gepostet.
Daniel Andersson
1

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.

Charles
quelle
FSlint finden Sie unter pixelbeat.org/fslint
mklement0
1

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 .hardlinkan, um Ihnen dabei zu helfen.

Markieren Sie Hardlinks

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'
Daniel Sokolowski
quelle