84

я бегу

ln /a/A /b/B

Я хотел бы видеть в папке и где точки файла А в a

10 ответов10

157

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

ls -i

а также

ls -l

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

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

find . -inum NUM

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

59

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

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

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

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

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
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
10

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

Если у вас есть определенный файл <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 {} \; 
4

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

find directories.. -xdev ! -type d -links +1 -printf '%20D %20i %p\n' | sort -n | uniq -w 42 --all-repeated=separate repeat = Отдельный (Спасибо @Tino за настройку моей оригинальной команды для поддержки FS-id (%D) и для обработки всех типов файлов, не относящихся к каталогам, а не только обычных файлов. Это найдет ваши многосвязные символические ссылки, каналы и т.д.)

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

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

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

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 s может быть просто grep .

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 "";
1

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

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

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

1

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

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

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

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .