Используйте basename для разбора списка путей в файле

9

Я использую Mac OSX и пытаюсь использовать командную строку, чтобы найти количество файлов с таким же именем.

Я попытался использовать следующую команду:

find ~ -type f -name "*" -print | basename | sort | uniq -d > duplicate_files

Это не работает! Когда я делаю следующее:

find ~ -type f -name "*" -print > duplicate_files

Тогда duplicate_files содержит пути ко всем моим файлам. Поэтому я думаю, что проблема в том, basenameчто он не принимает стандартный ввод. Затем я попробовал следующее:

basename $(find ~ -type f -name "*" -print) > duplicate_files

но опять же это не похоже на работу. Поиск в интернете, кажется, не приносит особой радости. Любые мысли приветствуются.

Johnb
источник

Ответы:

16

basename работает с аргументом командной строки, он не читает со стандартного ввода.

Вам не нужно вызывать basenameутилиту, и вам лучше этого не делать: все, что нужно сделать, это удалить часть перед последней /, и будет медленным вызывать внешнюю команду для каждой записи, вы можете использовать обработку текста утилита вместо.

find ~ -type f | sed 's!.*/!!' | sort | uniq -d

Может быть более полезно отслеживать местонахождение файлов. Сортировка по имени облегчает поиск дубликатов, но sortне позволяет использовать последнее поле. Что вы можете сделать, это скопировать последнее /разделенное поле в начало, затем отсортировать и затем использовать специальную обработку awk для извлечения и представления дубликатов.

find ~ -type f |
sed 's!.*/\(.*\)!\1/&!' |   # copy the last field to the beginning
sort -t/ -k1,1 |
cut -d/ -f2- |   # remove the extra first field (could be combined with awk below)
awk -F / '{
    if ($NF == name) {
        if (previous != "") {print previous; previous = ""}
        print
    } else {
        previous = $0
        name = $NF
    }
'

(Обратите внимание, что я предполагаю, что ни одно из ваших имен файлов не содержит символов новой строки.)

Жиль "ТАК - перестань быть злым"
источник
Супер спасибо. Это именно то, что я пытался сделать ... очень полезно
JohnB
7

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

find ~ -type f -printf '%f\n' | sort | uniq -c

(предполагает GNU find) или хотя бы что-то вроде этого:

find ~ -exec basename {} \; | sort | uniq -c

basename не может читать по каналу или обрабатывать несколько файлов одновременно.

пс. Нет необходимости указывать -name '*', хотите ли вы перечислить все файлы. Это опция по умолчанию.

порыв
источник
Спасибо - '-printf' не работает для OS X UNIX
JohnB
И когда я пробую вторую версию, я получаю basename: unknown primary or operator. Спасибо за подсказку на-name "*"
JohnB
Это странно. Я могу видеть -printfдаже на странице руководства posix. Насчет ошибки со вторым способом, это причина опечатки в моем ответе. Исправлена. Не могли бы вы попробовать это еще раз?
Раш
Также -printfя получаю -printf: unknown primary or operator. Кроме того, когда я проверял Unix в справочнике Nutshell, он указывался как опция GNU / Linux - ничего не говорится об OSX
JohnB
1
На самом деле лучший источник был бы man findв вашей консоли :)
пик
4

Кажется, это работает для меня на OSX:

find ~ -type f -exec basename -a {} + | sort | uniq -d
rahmu
источник
Да - это большое спасибо - из интереса, что +означает в команде?
JohnB
2
Это полезно, пожалуйста, подумайте об этом.
Подозреваемый
Это - я не могу голосовать, потому что мне нужно 15 репутации :-(
JohnB
@StephaneChazelas: Согласно man-странице для базового имени BSD , исполняемый файл может принимать несколько строк в качестве аргументов. Я дважды проверил на OSX, он работает.
Рахму
1
Ладно, извините, я исправлюсь. Я не знал об этом расширении BSD. Тем не менее, это все равно не работает, если есть ровно два файла. Вам также нужно добавить -aопцию для покрытия этого случая.
Стефан Шазелас
2

Альтернативы (предполагается, что в именах файлов нет новой строки):

find ~ -type f | awk -F/ '{print $NF}' | sort | uniq -d
Стефан Шазелас
источник
2

Вы можете использовать xargsс, basenameчтобы получить желаемый результат, например так:

find ~ -type f -name "*" -print | xargs -l basename | sort | uniq -d > duplicate_files
Seff
источник
0

В последней версии, bashкоторая обрабатывает ассоциативные массивы, следующее дополнительно будет обрабатывать имена путей со встроенными символами новой строки:

#!/bin/bash

topdir=$HOME

shopt -s globstar  # enable the ** glob

declare -A count

# count the number of times each filename (base name) occurs
for pathname in "$topdir"/**; do
    # skip names that are not regular files (or not symbolic links to such files)
    [ ! -f "$pathname" ] && continue

    # get the base name
    filename=${pathname##*/}

    # add one to this base name's count
    count[$filename]=$(( ${count[$filename]} + 1 ))
done

# go through the collected names and print any name that
# has a count greater than one
for filename in "${!count[@]}"; do
    if [ "${count[$filename]}" -gt 1 ]; then
        printf 'Duplicate filename: %s\n' "$filename"
    fi
done

Это не использует внешнюю утилиту.

Кусалананда
источник