Как написать однострочный bash, который найдет двоичные файлы с идентичным содержимым, разрешениями и владельцем в той же файловой системе ext4 рекурсивно из текущего рабочего каталога и заменит все файлы с более ранним временем доступа жесткими ссылками на последний доступный файл и отчет о сэкономленном дисковом пространстве в кибибайтах?

То, что я достиг до сих пор, не полностью соответствует требованиям цели.

#! /bin/sh
fdupes -r -p -o 'time' . | xargs file -i | grep binary | awk '{print $1}' | awk '{print substr($0,3)}' | sed 's/.\{1\}$//' | xargs rdfind -makehardlinks true
1
AdminBee 14 Окт 2020 в 11:35
1
Чего не хватает в вашем текущем решении, пока все ваши требования не будут выполнены?
 – 
thanasisp
12 Окт 2020 в 21:41
Жесткие ссылки файлов не создаются на основе владельца в rdfind.
 – 
Himadri Ganguly
12 Окт 2020 в 21:58
1
Для этого есть команда hardlink. Прочтите man hardlink.
 – 
waltinator
13 Окт 2020 в 04:11
Но у жесткой ссылки нет функций, которые требуются для этой проблемы.
 – 
Himadri Ganguly
13 Окт 2020 в 07:24
В то время как fdupes возвращает группы файлов (в режиме абзаца или с использованием -1 в строке), вы теряете эту группировку после первой следующей команды, и она вам понадобится для любой обработки позже, где вы укажете жесткую ссылку ? К первому или последнему, согласно вашему временному порядку, этой группы файлов. Также должны быть сохранены имена файлов.
 – 
thanasisp
13 Окт 2020 в 07:27

2 ответа

Лучший ответ

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

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

Таким образом, чтобы быть уверенным, что точные выборки fdupes будут жестко связаны, мы должны вызывать hardlink отдельно один раз для каждого абзаца. Чтобы не было случая, когда две пары одинаковых одинаковых существуют для разных владельцев или с разными разрешениями. И, конечно, файлы должны быть отфильтрованы для двоичных файлов.

#!/bin/bash
unset arr i
while IFS= read -r f; do

    # move file to array if binary
    if file -i "$f" | grep -q "charset=binary"; then
        arr[++i]="$f"
    fi
    
    # if end of paragraph and array has files, hardlink and unset array
    if [[ "$f" == "" && "${arr[@]}" ]]; then
        printf "\n => Hardlink for %d files:\n" "$i"
        hardlink -n -c -vv "${arr[@]}"
        unset arr i
    fi

done < <(fdupes -rpio time .)

hardlink с параметром -n имитирует и ничего не записывает, поэтому протестируйте приведенное выше как есть и удалите -n позже.

Также не обрабатываются имена файлов с новыми строками, тестирование с пробелами кажется нормальным.

0
thanasisp 14 Окт 2020 в 11:34
Но когда вы загружаете файлы в жесткую ссылку, вы не можете различать файлы от разных пользователей.
 – 
Himadri Ganguly
13 Окт 2020 в 13:53
Вариант использования: файл A и файл B идентичны в отношении содержимого, разрешений и владельца USER1, а время доступа к файлу A является последним. Другой файл C и файл D идентичны в отношении содержимого, разрешений, а владельцем является USER2, а время доступа к файлу D является последним. Затем файл B будет заменен жесткой ссылкой файла A, а файл C — жесткой ссылкой файла D.
 – 
Himadri Ganguly
13 Окт 2020 в 20:58
Большое спасибо, приведенный выше скрипт выполняет большую часть работы. Но будет ли считаться однострочным bash-скрипт и как мы можем отобразить сохраненный размер в кибибайтах?
 – 
Himadri Ganguly
14 Окт 2020 в 16:07
Если этот ответ поможет вам, вы также можете проверить это. Вывод также имеет очень хороший отчет -vv, который можно анализировать, сохранять, изменять и т. д.
 – 
thanasisp
14 Окт 2020 в 19:36

Наконец-то получил желаемый результат. Спасибо @thanasisp. Для этого вам понадобятся две программы fdupes и rdfind.

#!/bin/bash
unset arr i; while IFS= read -r f; do if file -i "$f" | grep -q "charset=binary"; then arr[++i]="$f"; fi; if [[ "$f" == "" && "${arr[@]}" ]]; then printf "\n => Hardlink for %d files:\n" "$i";rdfind -makehardlinks true "${arr[@]}" | grep "Total size is" | grep -P "[0-9]+" -o  | head -1 | awk -v count="$i" '{print $1/count;}' | awk '{printf("%s kibibytes saved.\n",$1/1024)}'; unset arr i; fi; done < <(fdupes -rpio time .)
0
Himadri Ganguly 15 Окт 2020 в 09:44