Как вы можете увидеть реальную жесткую ссылку по ls?

97

я бегу

ln /a/A /b/B

Я хотел бы видеть в папке, aгде файл A указывает на ls.

Лео Леопольд Герц 준영
источник
1
Жесткие ссылки не являются указателями, символические ссылки. Это несколько имен для одного и того же файла (inode). После link(2)системного вызова не имеет смысла, кто является оригиналом, а кто - ссылкой. Вот почему, как показывают ответы, единственный способ найти все ссылки find / -samefile /a/A. Потому что одна запись каталога для inode не «знает» о других записях каталога для того же inode. Все, что они делают, это пересчитывают индекс, чтобы он мог быть удален, когда фамилия для него unlink(2)ed. (Это «количество ссылок» в lsвыходных данных).
Питер Кордес
@PeterCordes: На самом ли деле учетная запись хранится в записи жесткой ссылки? Это то, что подразумевает ваша формулировка («Все, что они делают, это пересчитывают инод ...») Но это не имело бы смысла, если бы ссылки ничего не знали друг о друге, поскольку при обновлении одной все остальные каким-то образом должны быть обновленным. Или рефконт хранится в самом иноде? (Простите, если это глупый вопрос, я считаю себя новичком, и я все еще учусь).
одинокий катер
1
Рефконт хранится в иноде, как вы в конечном итоге выяснили, должно быть, исходя из других фактов. :) Записи в каталоге называются указателями на иноды. Мы называем это «жесткой связью», когда у вас есть несколько имен, указывающих на один и тот же индекс.
Питер Кордес

Ответы:

171

Вы можете найти номер инода для вашего файла с помощью

ls -i

а также

ls -l

показывает количество ссылок (количество жестких ссылок на определенный индекс)

после того как вы нашли номер инода, вы можете искать все файлы с одинаковым инодом:

find . -inum NUM

покажет имена файлов для inode NUM в текущем каталоге (.)

ZZR
источник
46
Вы можете просто запустить найти. -samefile имя файла
BeowulfNode42
1
@ BeowulfNode42 Эта команда хороша, но для нее нужна как минимум общая корневая папка с теми же файлами.
Итачи
1
этот ответ дает прагматичное «сделай это», но я твердо убежден, что @LaurenceGonsalves отвечает на вопросы «как» и / или «почему».
Тревор Бойд Смит
65

На самом деле нет четко определенного ответа на ваш вопрос. В отличие от символических ссылок, жесткие ссылки неотличимы от «оригинального файла».

Записи каталога состоят из имени файла и указателя на индекс. Индод, в свою очередь, содержит метаданные файла и (указатели на) фактическое содержимое файла). Создание жесткой ссылки создает другое имя файла + ссылку на тот же индекс. Эти ссылки являются однонаправленными (по крайней мере, в типичных файловых системах) - индекс хранит только счетчик ссылок. Не существует внутреннего способа узнать, какое имя файла является «оригинальным».

Кстати, именно поэтому системный вызов «удалить» файл называется unlink. Это просто удаляет жесткую ссылку. Индод, к которому прикреплены данные, удаляется только в том случае, если счетчик ссылок инода падает до 0.

Единственный способ найти другие ссылки на данный индекс - это провести тщательный поиск в файловой системе, проверяя, какие файлы ссылаются на рассматриваемый индекс. Вы можете использовать 'test A -ef B' из оболочки для выполнения этой проверки.

Лоуренс Гонсалвес
источник
35
Это означает, что не существует такой вещи, как жесткая ссылка на другой файл , поскольку исходный файл также является жесткой ссылкой; жесткие ссылки указывают на местоположение на диске .
jtbandes
12
@jtbandes: жесткие ссылки указывают на индекс, который указывает на фактические данные.
dash17291
33

UNIX имеет жесткие ссылки и символические ссылки (сделаны с "ln"и "ln -s"соответственно). Символические ссылки - это просто файл, который содержит реальный путь к другому файлу и может пересекать файловые системы.

Жесткие ссылки существуют с самых первых дней существования UNIX (я все равно помню, и это довольно давно). Это две записи каталога , которые ссылаются на точные же данные. Данные в файле определяются его inode. Каждый файл в файловой системе указывает на индекс, но нет требования, чтобы каждый файл указывал на уникальный индекс - отсюда и жесткие ссылки.

Поскольку inode уникальны только для данной файловой системы, существует ограничение, что жесткие ссылки должны быть в одной файловой системе (в отличие от символических ссылок). Обратите внимание, что в отличие от символьных ссылок, нет привилегированного файла - все они равны. Область данных будет освобождена только тогда, когда все файлы, использующие этот индекс, будут удалены (и все процессы также закроют ее, но это другая проблема).

Вы можете использовать "ls -i"команду, чтобы получить индекс определенного файла. Затем вы можете использовать "find <filesystemroot> -inum <inode>"команду, чтобы найти все файлы в файловой системе с указанным индексом.

Вот скрипт, который делает именно это. Вы вызываете это с помощью:

findhardlinks ~/jquery.js

и он найдет все файлы в этой файловой системе, которые являются жесткими ссылками для этого файла:

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

Вот сценарий.

#!/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

источник
@pax: Кажется, в скрипте есть ошибка. Я начинаю это, . ./findhardlinks.bashнаходясь в OS X Zsh. Мое текущее окно на экране закрывается.
4
@Masi Проблема в твоих словах. (так же, как исходная команда). Это заставляет команду exit 1 выходить из вашей оболочки. Используйте chmod a + x findhardlinks.bash, затем запустите его с помощью ./findhardlinks.bash или используйте bash findhardlinks.bash
njsf
Пожалуйста, смотрите мой ответ на ваш ответ по адресу superuser.com/questions/12972/to-see-hardlinks-by-ls/…
Léo Léopold Hertz 준영
3
Чтобы сделать это программно, это, вероятно , более устойчивыми , если вы используете вместо этого: INUM=$(stat -c %i $1). Также NUM_LINKS=$(stat -c %h $1). См. man statБольше переменных формата, которые вы можете использовать.
Джо
Лучший ответ, безусловно. Престижность.
MariusMatutiae
24
ls -l

Первый столбец будет представлять разрешения. Во втором столбце будет указано количество подпунктов (для каталогов) или количество путей к тем же данным (жесткие ссылки, включая исходный файл) к файлу. Например:

-rw-r--r--@    2    [username]    [group]    [timestamp]     HardLink
-rw-r--r--@    2    [username]    [group]    [timestamp]     Original
               ^ Number of hard links to the data
eyelidlessness
источник
2
Полезно для определения, если данный файл имеет [другие] жесткие ссылки, но не ГДЕ они являются.
mklement0
Кроме того, нет никаких технических различий между жесткой ссылкой и исходным файлом. Они оба идентичны тем, что они просто указывают на то, inodeчто в свою очередь указывает на содержимое диска.
Гайарад
13

Как насчет следующего более простого? (Последние могут заменить длинные сценарии выше!)

Если у вас есть конкретный файл <THEFILENAME>и вы хотите знать все его жесткие ссылки, распределенные по каталогу <TARGETDIR>(который может быть даже обозначен всей файловой системой /)

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

Расширяя логику, если вы хотите знать все файлы в <SOURCEDIR>нескольких жестких ссылках <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потому что файл тоже может быть каталогом.
Сильвио
3
@silvio: Вы можете создавать жесткие ссылки только на файлы , а не на каталоги.
mklement0
@ mklement0: Вы правы!
Сильвио
.И ..записи в каталогах жесткие ссылки. Вы можете определить, сколько подкаталогов находится в каталоге, по количеству ссылок .. В любом случае это спорный вопрос, так как find -samefile .все равно не будет выводить subdir/..вывод. find(по крайней мере, версия GNU) кажется жестко закодированным, чтобы игнорировать .., даже с -noleaf.
Питер Кордес
кроме того, идея find-all-links есть O(n^2)и выполняется findодин раз для каждого члена набора жестко связанных файлов. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separateбудет работать (16 недостаточно для десятичного представления 2 ^ 63-1, поэтому, когда ваша файловая система XFS достаточно велика, чтобы иметь столь высокие номера инодов, будьте внимательны)
Питер Кордес
5

Есть много ответов со скриптами, чтобы найти все жесткие ссылки в файловой системе. Большинство из них делают глупые вещи, такие как запуск find для сканирования всей файловой системы на -samefileпредмет КАЖДОГО многосвязного файла. Это безумие; все, что вам нужно, это отсортировать по номеру инода и распечатать дубликаты.

Только один проход по файловой системе, чтобы найти и сгруппировать все наборы жестко связанных файлов

find dirs   -xdev \! -type d -links +1 -printf '%20D %20i %p\n' |
    sort -n | uniq -w 42 --all-repeated=separate

Это намного быстрее, чем другие ответы для поиска нескольких наборов жестко связанных файлов.
find /foo -samefile /barотлично подходит только для одного файла.

  • -xdev: ограничение на одну файловую систему. Строго не требуется, так как мы также печатаем идентификатор FS в uniq на
  • ! -type dотклонять каталоги: .и ..запись означает , что они всегда связаны между собой .
  • -links +1 : количество ссылок строго > 1
  • -printf ...выведите FS-id, номер индекса и путь. (С дополнением к фиксированной ширине столбца, о котором мы можем рассказать uniq.)
  • sort -n | uniq ... числовая сортировка и унификация по первым 42 столбцам, разделяя группы пустой строкой

Использование ! -type d -links +1означает, что входные данные сортировки настолько же велики, как и конечные выходные данные uniq, поэтому мы не занимаемся огромной сортировкой строк. Если вы не запустите его в подкаталоге, который содержит только один из набора жестких ссылок. В любом случае, для повторного обхода файловой системы потребуется гораздо меньше процессорного времени, чем для любого другого опубликованного решения.

образец вывода:

...
            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 ?: распаковать вывод с помощью awkили cut. uniqимеет очень ограниченную поддержку выбора полей, поэтому я дополняю результаты поиска и использую фиксированную ширину. 20 символов достаточно широки для максимально возможного индекса или номера устройства (2 ^ 64-1 = 18446744073709551615). XFS выбирает номера инодов в зависимости от того, где на диске они расположены, а не от 0, поэтому большие файловые системы XFS могут иметь> 32-битные номера инодов, даже если у них нет миллиардов файлов. Другие файловые системы могут иметь 20-значные номера инодов, даже если они не гигантские.

TODO: сортировка групп дубликатов по пути. Сортировка их по точке монтирования, а затем по номеру инода смешивает вещи, если у вас есть пара разных подкаталогов, которые имеют много жестких ссылок. (то есть группы дуп-групп объединяются, но вывод смешивает их).

Финал sort -k 3сортирует строки отдельно, а не группы строк как одну запись. Предварительная обработка чего-либо для преобразования пары новых строк в байт NUL и использование GNU sort --zero-terminated -k 3могут помочь. trработает только с одиночными символами, но не с 2-> 1 или 1-> 2 шаблонами. perlбудет делать это (или просто разобрать и отсортировать в perl или awk). sedможет также сработать.

Питер Кордес
источник
1
%Dидентификатор файловой системы (она является уникальным для текущей загрузки , а не файловые системы не umountэд), поэтому следующий еще более общий характер : find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate. Это работает до тех пор, пока ни один каталог не содержит другой каталог на уровне файловой системы, а также он смотрит на все, что может быть жестко связано (например, устройства или программные ссылки - да, программные ссылки могут иметь количество ссылок больше 1). Обратите внимание, что dev_tи сегодня ino_tэто 64 бит. Это, вероятно, будет продолжаться до тех пор, пока у нас есть 64-битные системы.
Тино
@Tino: отличная идея об использовании ! -type dвместо -type f. У меня даже есть жесткие ссылки в моей файловой системе для организации некоторых коллекций файлов. Обновил мой ответ с вашей улучшенной версией (но я сначала поставил fs-id, так что порядок сортировки по крайней мере группируется по файловой системе.)
Питер Кордес
3

Это своего рода комментарий к собственному ответу и сценарию Торокоро-Мачо, но он явно не помещается в поле для комментариев.


Переписал ваш сценарий, используя более простые способы поиска информации и, таким образом, значительно меньше вызовов процессов.

#!/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

Я старался сделать его максимально похожим на ваш, чтобы его можно было легко сравнить.

Комментарии к этому и вашему сценарию

  • Следует всегда избегать $IFSмагии, если достаточно глобуса, поскольку он излишне запутан, а имена файлов на самом деле могут содержать символы новой строки (но на практике в основном это первая причина).

  • Вы должны избегать ручного разбора lsи такого вывода в максимально возможной степени, так как это рано или поздно укусит вас. Например: в первой awkстроке вы ошибаетесь во всех именах файлов, содержащих пробелы.

  • printfв конце концов, часто избавляет от неприятностей, так как он очень устойчив с %sсинтаксисом. Он также дает вам полный контроль над выходом и в отличие от всех систем echo.

  • stat может сэкономить вам много логики в этом случае.

  • GNU find это мощный

  • Ваши вызовы headи tailвызовы могли быть обработаны напрямую, awkнапример, с помощью exitкоманды и / или выбора NRпеременной. Это сохранит вызовы процессов, что почти всегда значительно повышает производительность в трудолюбивых сценариях.

  • Ваши egrepс тем же успехом могут быть просто grep.

Даниэль Андерссон
источник
xDEVICE = $ (stat -c% m "$ {xFILE}") работает не на всех системах (например: stat (GNU coreutils) 6.12). Если скрипт выдает «Item:?» в начале каждой строки, затем замените эту оскорбительную строку строкой, более похожей на оригинальный скрипт, но с xITEM, переименованным в xFILE: xDEVICE = $ (df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien
Если вам просто нужны группы жестких ссылок, а не повторяющиеся с каждым участником в качестве «мастера», используйте find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate. Это НАМНОГО быстрее, так как проходит только один раз. Для нескольких FS, вам нужно префикс номера Inode с идентификатором FS. Возможно сfind -exec stat... -printf ...
Питер Кордес
превратил эту идею в ответ
Питер Кордес
2

Основываясь на findhardlinksсценарии (переименовал его hard-links), это то, что я реорганизовал и заставил его работать.

Выход:

# ./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
источник
Я разместил комментарии к этому сценарию в качестве отдельного ответа.
Даниэль Андерссон
1

Решение с графическим интерфейсом действительно близко к вашему вопросу:

Вы не можете перечислить фактические жестко связанные файлы из «ls», потому что, как указывали предыдущие комментаторы, «имена» файлов являются просто псевдонимами к тем же данным. Тем не менее, на самом деле есть инструмент с графическим интерфейсом, который очень близок к тому, что вы хотите, который отображает список путей имен файлов, которые указывают на те же данные (как жесткие ссылки) в Linux, он называется FSLint. Требуемая опция находится в разделе «Конфликты имен» -> снимите флажок «$ PATH» в разделе «Поиск (XX) ->» и выберите «Псевдонимы» в раскрывающемся списке после «для ...» по направлению к верхней середине.

FSLint очень плохо документирован, но я обнаружил, что удостоверился, что ограниченное дерево каталогов в разделе «Путь поиска» с установленным флажком «Recurse?» и вышеупомянутые опции, список жестко связанных данных с путями и именами, которые «указывают» на одни и те же данные, создаются после поиска программы.

Чарльз
источник
FSlint можно найти по адресу pixelbeat.org/fslint
mklement0
1

Вы можете настроить lsвыделение жестких ссылок с помощью «псевдонима», но, как было сказано ранее, нет способа показать «источник» жесткой ссылки, поэтому я добавляю, .hardlinkчтобы помочь с этим.

выделить жесткие ссылки

Добавьте следующее где-то в вашем .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
Даниэль Соколовский
источник