Використовуйте базове ім'я для розбору списку шляхів у файлі


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не має можливості використовувати останнє поле. Що ви можете зробити, це скопіювати останнє /розділене поле на початок, потім сортувати, а потім використати трохи спеціальних обробок для вилучення та подання дублікатів.

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 man". Про помилку з другим способом, це причина помилки в моїй відповіді. Виправлено. Не могли б ви спробувати ще раз?
пік

Також з -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, він працює.
rahmu

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.