Użyj basename, aby przeanalizować listę ścieżek przechowywanych w pliku


9

Korzystam z systemu Mac OSX i próbuję użyć wiersza polecenia, aby znaleźć liczbę plików o tej samej nazwie.

Próbowałem użyć następującego polecenia:

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

To nie działa! Kiedy wykonam następujące czynności:

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

Następnie duplicate_files zawiera ścieżki wszystkich moich plików. Myślę więc, że problem dotyczy basename- nie akceptuje standardowych danych wejściowych. Następnie spróbowałem:

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

ale znowu to nie działa. Wyszukiwanie w Internecie nie sprawia wiele radości. Wszelkie myśli są mile widziane.

Odpowiedzi:


16

basename działa na argumencie wiersza poleceń, nie odczytuje ze standardowego wejścia.

Nie musisz dzwonić do basenamenarzędzia, a lepiej nie: wystarczy, że usuniesz część przed ostatnim /, a powolne byłoby wywoływanie zewnętrznego polecenia dla każdego wpisu, możesz użyć przetwarzania tekstu zamiast tego narzędzie.

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

Przydatne może być śledzenie lokalizacji plików. Sortowanie według nazwy ułatwia zlokalizowanie duplikatów, ale sortnie ma opcji użycia ostatniego pola. To, co możesz zrobić, to skopiować ostatnie /oddzielne pole na początek, a następnie posortować, a następnie użyć trochę przetwarzania ad hoc awk, aby wyodrębnić i przedstawić duplikaty.

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
    }
'

(Zauważ, że zakładam, że żadna z twoich nazw plików nie zawiera znaków nowej linii).


Super dzięki. Właśnie to starałem się zrobić ... bardzo przydatne
JohnB

7

Dlaczego nie użyć wbudowanych findfunkcji, aby wyprowadzić tylko nazwę pliku:

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

(zakłada GNU find) lub przynajmniej coś takiego:

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

basename nie można czytać za pomocą potoku ani przetwarzać wielu plików jednocześnie.

ps. Nie ma potrzeby określania, -name '*'czy chcesz wyświetlić listę wszystkich plików. To jest opcja domyślna.


Dzięki - „-printf” nie działa w systemie OS X UNIX
JohnB

A kiedy próbuję drugiej wersji, dostaję basename: unknown primary or operator. Dzięki za wskazówkę-name "*"
JohnB

To jest dziwne. Widzę -printfnawet na stronie man posix. Jeśli chodzi o błąd z drugą drogą, w mojej odpowiedzi jest to literówka. Naprawiony. Czy możesz spróbować jeszcze raz?
pędzi

Również z -printfdostaję -printf: unknown primary or operator. Również, gdy sprawdziłem Uniksa w książce Nutshell, wymieniono go jako opcję GNU / Linux - nie mówi nic o OSX
JohnB

1
Właściwie najlepsze źródło byłoby man findw twojej konsoli :)
pędzi

4

Wygląda na to, że działa na mnie w OSX:

find ~ -type f -exec basename -a {} + | sort | uniq -d

Tak - to wielkie dzięki - poza zainteresowaniem, co +oznacza polecenie?
JohnB

2
Czy jest to przydatne, proszę rozważyć to.
podejrzany

To znaczy - nie mogę głosować, ponieważ potrzebuję 15 reputacji :-(
JohnB

@StephaneChazelas: Zgodnie ze stroną podręcznika dla nazwy basenowej BSD plik wykonywalny może przyjmować wiele ciągów znaków jako argumentów. Sprawdziłem dwukrotnie OSX, działa.
rahmu

1
W porządku przepraszam, poprawiam się. Nie wiedziałem o tym rozszerzeniu BSD. Jednak nadal nie powiedzie się, jeśli są dokładnie dwa pliki. Trzeba też dodać -aopcję pokrycia tej sprawy.
Stéphane Chazelas,

2

Alternatywy (zakłada brak nowej linii w nazwach plików):

find ~ -type f | awk -F/ '{print $NF}' | sort | uniq -d

2

Możesz użyć za xargspomocą, basenameaby uzyskać pożądane dane wyjściowe, takie jak:

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

0

W najnowszej wersji bashtej obsługi tablic asocjacyjnych następujące czynności dodatkowo obsługiwałyby ścieżki z osadzonymi znakami nowej linii:

#!/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

To nie używa zewnętrznego narzędzia.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.