Используйте 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

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

Ответы:


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

Да - это большое спасибо - из интереса, что +означает в команде?
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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.