Linux: найти все символические ссылки данного «исходного» файла? (обратная «ссылка для чтения»)

Рассмотрим следующий фрагмент командной строки:

$ cd /tmp/
$ mkdir dirA
$ mkdir dirB
$ echo "the contents of the 'original' file" > orig.file
$ ls -la orig.file 
-rw-r--r-- 1 $USER $USER 36 2010-12-26 00:57 orig.file

# create symlinks in dirA and dirB that point to /tmp/orig.file:

$ ln -s $(pwd)/orig.file $(pwd)/dirA/
$ ln -s $(pwd)/orig.file $(pwd)/dirB/lorig.file
$ ls -la dirA/ dirB/
dirA/:
total 44
drwxr-xr-x  2 $USER $USER  4096 2010-12-26 00:57 .
drwxrwxrwt 20 root          root          36864 2010-12-26 00:57 ..
lrwxrwxrwx  1 $USER $USER    14 2010-12-26 00:57 orig.file -> /tmp/orig.file

dirB/:
total 44
drwxr-xr-x  2 $USER $USER  4096 2010-12-26 00:58 .
drwxrwxrwt 20 root          root          36864 2010-12-26 00:57 ..
lrwxrwxrwx  1 $USER $USER    14 2010-12-26 00:58 lorig.file -> /tmp/orig.file

В этот момент я могу использовать readlink, чтобы увидеть, что является «оригиналом» (ну, я полагаю, что обычный термин здесь — либо «цель», либо «источник», но в моем уме это могут быть и противоположные понятия, поэтому я' просто назовем его "оригинальным") файл символических ссылок, т.е.

$ readlink -f dirA/orig.file 
/tmp/orig.file
$ readlink -f dirB/lorig.file 
/tmp/orig.file

... Однако я хотел бы знать, есть ли команда, которую я мог бы запустить для «исходного» файла и найти все символические ссылки, указывающие на него? Другими словами, что-то вроде (псевдо):

$ getsymlinks /tmp/orig.file
/tmp/dirA/orig.file 
/tmp/dirB/lorig.file

Заранее спасибо за любые комментарии,

Ваше здоровье!


person sdaau    schedule 26.12.2010    source источник


Ответы (6)


Я не видел команды для этого, и это непростая задача, поскольку целевой файл содержит нулевую информацию о том, какие исходные файлы указывают на него.

Это похоже на «жесткие» ссылки, но, по крайней мере, они всегда находятся в одной и той же файловой системе, поэтому вы можете сделать find -inode, чтобы перечислить их. Мягкие ссылки более проблематичны, поскольку они могут пересекать файловые системы.

Я думаю, что вам нужно будет выполнить ls -al для каждого файла во всей вашей иерархии и использовать grep для поиска -> /path/to/target/file.

Например, вот тот, который я запустил в своей системе (отформатирован для удобочитаемости — последние две строки на самом деле занимают одну строку в реальном выводе):

pax$ find / -exec ls -ald {} ';' 2>/dev/null | grep '\-> /usr/share/applications'
lrwxrwxrwx 1 pax pax 23 2010-06-12 14:56 /home/pax/applications_usr_share
                                         -> /usr/share/applications
person paxdiablo    schedule 26.12.2010
comment
Привет @paxdiablo - также спасибо за четкий ответ; + принять ссылку на «жесткие ссылки» и «find -inode». Ваше здоровье! - person sdaau; 26.12.2010
comment
Не забывайте, что вы можете иметь символические ссылки на серверах, где fs смонтирована по nfs. Так что может быть недостаточно просто посмотреть на локальный сервер. - person dietbuddha; 26.12.2010
comment
Это не будет работать ни с рекурсивными ссылками (ссылки на ссылки на...), ни с относительными ссылками (что угодно -> ../../usr/share/applications). С такими делами работать сложнее. Кроме того, он порождает новый экземпляр ls для каждого проверенного файла, что означает, что это будет довольно медленно. - person Gordon Davisson; 26.12.2010
comment
@ Гордон, ты прав, это не будет работать как есть для более длинных цепочек, но мне не ясно, что это было требованием. Вы можете сделать скрипт, который будет следовать таким цепочкам, если хотите, но, как и символические ссылки, смонтированные в NFS, о которых упоминал диетический будда, это другой уровень проблем. Что касается эффективности, хорошо известно (или, по крайней мере, в другом месте на SO), что find с xargs лучше для эффективности, но это не относится к конкретному вопросу здесь. Если это окажется слишком медленным, то непременно спросите, как сделать это быстрее. Но, если это одноразовая вещь, она, вероятно, не нужна. - person paxdiablo; 26.12.2010
comment
Привет @dietbuddha, @Gordon Davisson, @paxdiablo - спасибо, что записали эти вопросы! Просто хотел повторить это, действительно - для моего вопроса эффективность не имеет особого значения; Я просто хотел знать в принципе, как будет обрабатываться «обратная ссылка для чтения». Еще раз спасибо - ура! - person sdaau; 26.12.2010
comment
@paxdiablo для скорости, как насчет использования find / -type l -ls? Это пропускает несимволические ссылки и позволяет избежать создания всех процессов для каждого файла (что делает его быстрее, чем мой ответ). P.S. причина, по которой я так беспокоюсь о скорости, заключается в том, что на моем загрузочном томе 824 220 файлов, и если вы создадите процесс для каждого из них... это займет некоторое время. - person Gordon Davisson; 26.12.2010
comment
Я слышал, что обработка вывода команды ls — один из семи смертных грехов. - person JeffG; 24.02.2012
comment
Да, иногда это трудно сделать правильно, особенно поскольку некоторые версии ls имеют привычку изменять свой вывод в зависимости от того, отправляют ли они на терминал или нет. А также потому, что большинство людей используют его для работы с именами файлов без обработки пробелов. Но это решение не подвержено этим двум проблемам, и, если вы понимаете формат данных, которые создает ls, это не больше зла, чем любая другая команда :-) - person paxdiablo; 25.02.2012

Используя GNU find, это найдет файлы, которые жестко связаны или символически связаны с файлом:

find -L /dir/to/start -samefile /tmp/orig.file
person Dennis Williamson    schedule 26.12.2010
comment
Привет, @Dennis Williamson, спасибо, +1 за ответ! :) Кстати, как это связано с приведенным выше ...целевой файл содержит нулевую информацию о том, какие исходные файлы указывают на него...; ...в основном выполнять ls -al для каждого файла...?? Делает ли эта команда в основном то же самое (но, возможно, более оптимизированным образом) или что-то принципиально другое? Еще раз спасибо, ура! - person sdaau; 04.01.2011
comment
@sdaau: целевой файл не содержит информации. Эта команда, по сути, является поиском файлов, которые ссылаются на нее. - person Dennis Williamson; 04.01.2011
comment
Есть ли эквивалентный способ сделать это с помощью BusyBox Find? - person Gavin S. Yancey; 21.07.2016
comment
@g.rocket: Чтобы найти файлы с жесткими ссылками: find /dir/to/start -follow -inum $(stat -c %i /tmp/orig.file). Вы могли бы придумать что-нибудь, чтобы найти символические ссылки, но мне быстро ничего не приходит в голову. - person Dennis Williamson; 21.07.2016
comment
есть ли аналог этого каталога? - person qodeninja; 20.08.2019
comment
@qodeninja: Если имя, которое является аргументом -samefile, является каталогом, то результатом будут каталоги (или символические ссылки на каталоги). Если вы хотите быть явным, добавьте -type d в команду после позиции, в которой указан начальный каталог, например: find -L /dir/to/start type -d -samefile /tmp/orig.dir - person Dennis Williamson; 20.08.2019

Символические ссылки не отслеживают, что указывает на заданное место назначения, поэтому вы не можете добиться большего успеха, чем проверять каждую символическую ссылку, чтобы увидеть, указывает ли она на желаемое место назначения, например

for i in *; do
    if [ -L "$i" ] && [ "$i" -ef /tmp/orig.file ]; then
        printf "Found: %s\n" "$i"
    fi
done
person jilles    schedule 26.12.2010

Вот что я придумал. Я делаю это в OS X, в которой нет readlink -f, поэтому мне пришлось использовать вспомогательную функцию, чтобы заменить ее. Если у вас есть правильный readlink -f, вы можете использовать его вместо этого. Кроме того, в этом случае использование while ... done < <(find ...) строго не требуется, подойдет и простой find ... | while ... done; но если вы когда-нибудь захотите сделать что-то вроде установки переменной внутри цикла (например, количества совпадающих файлов), версия канала завершится ошибкой, потому что цикл while будет выполняться в подоболочке. Наконец, обратите внимание, что я использую find ... -type l, поэтому цикл выполняется только для символических ссылок, а не для файлов других типов.

# Helper function 'cause my system doesn't have readlink -f
readlink-f() {
    orig_dir="$(pwd)"
    f="$1"
    while [[ -L "$f" ]]; do
        cd "$(dirname "$f")"
        f="$(readlink "$(basename "$f")")"
    done
    cd "$(dirname "$f")"
    printf "%s\n" "$(pwd)/$(basename "$f")"
    cd "$orig_dir"
}

target_file="$(readlink-f "$target_file")" # make sure target is normalized

while IFS= read -d '' linkfile; do
    if [[ "$(readlink-f "$linkfile")" == "$target_file" ]]; then 
        printf "%s\n" "$linkfile"
    fi
done < <(find "$search_dir" -type l -print0)
person Gordon Davisson    schedule 26.12.2010
comment
Привет @Gordon Davisson - спасибо за код; это также будет полезно для пользователей Mac! Ваше здоровье! - person sdaau; 26.12.2010

Вдохновлен комментарием Гордона Дэвиссона. Это похоже на другой ответ, но я получил желаемые результаты, используя exec. Мне нужно было что-то, что могло бы найти символические ссылки, не зная, где находится исходный файл.

find / -type l -exec ls -al {} \; | grep -i "all_or_part_of_original_name"
person Jer    schedule 14.04.2018

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

из корня вашего каталога хранилища данных или каталогов данных пользователей, где бы ни находились symlinked «файлы» до orig.file, запустите команду find:

# find -type l -ls |grep -i 'orig.file' 

or

# find /Starting/Search\ Path/ -type l -ls |grep -i '*orig*'

Обычно я бы использовал часть имени, например, '*orig*' для начала, потому что мы знаем, что пользователи будут переименовывать (префикс) просто названный файл в более описательный, например, «Jan report from London _ orig.file.2015.01.21» или что-то в этом роде.

Примечание. У меня никогда не работала опция -samefile.

чистый, простой, легко запоминающийся

надеюсь, что это поможет кому-то. Лэндис.

person Landis Reed    schedule 20.12.2015