Jak usunąć (1) z nazw plików za pomocą polecenia find

13

Niedawno przekonwertowałem wszystkie moje pliki FLAC na niższą częstotliwość próbkowania 44,1 kHz i głębię bitową 24 bity (ponieważ iPhone / iPod nie obsługują niczego powyżej) za pomocą XLD na moim Mac OS 10.7 (Lion).

Chociaż powiedziałem XLD, aby nadpisał wszystkie poprzednie pliki, XLD dodał (1)na końcu bardzo podobny plik z

some_song.m4a

do

some_song(1).m4a

Teraz chcę usunąć to (1)ze wszystkich przekonwertowanych plików FLAC.

Wiem, że prawdopodobnie mogłem użyć jakiegoś programu lub nawet AppleScript do zmiany nazwy plików, ale chciałem nauczyć się używać starej linii poleceń.

Wiem, że find . -name *\(1\).m4apobierze cały przekonwertowany plik FLAC.

Następnie wiem, że muszę coś zrobić -execi mvzmienić nazwy wszystkich znalezionych plików. Ale nie mogę zrozumieć, jak zachować oryginalną nazwę pliku i usunąć tylko (1).

Może muszę zrobić przechwytywanie wyrażeń regularnych grup, aby zapisać część nazwy pliku, której nie chcę modyfikować? A może nie da się zrobić wszystkiego w jednym wierszu i powinienem utworzyć skrypt powłoki (co nie jest tak wygodne, ale chętnie spróbuję).

Wszelkie wskazówki i sugestie są mile widziane! Dzięki!

hobbes3
źródło
5
Dlaczego głosowanie negatywne? Wydaje się, że jest to ważne pytanie ...
niezwiązane z twoim konkretnym pytaniem (on find), ale może to rozwiązać Twój rzeczywisty problem (konwersja plików audio), możesz zainteresować się audiotools.sourceforge.net i przykładem tego (dla lwa Macosx
meduz

Odpowiedzi:

13

Nie próbuj analizować finddanych wyjściowych, chyba że w ostateczności. Ważne jest, aby zdawać sobie sprawę, że w systemach plików Unix nazwy plików nie są ciągami znaków (powszechne nieporozumienie), ale raczej binarnymi blokami BLOB, które mogą zawierać dowolny znak oprócz /znaku null. Bezpieczna i prawidłowa analiza nazw plików jest wystarczającym problemem, że w 99% przypadków będziesz chciał całkowicie tego uniknąć (wystarczy spojrzeć na to, jak owłosione jest sedwyrażenie w odpowiedzi @ yarka, a nawet to nie obejmuje wszystkich przypadków) . Na szczęście w tym przypadku istnieje znacznie prostsze podejście:

find . -name '*(1).m4a' -execdir sh -c \
'for arg; do mv "$arg" "${arg%(1).m4a}".m4a; done' _ {} \+
jw013
źródło
schludne podejście jeszcze owłosione jak sed wyrażenie :) +1
yrk
1
czegoś tu brakuje ... gdzie done?
yrk
@yarek Dzięki za połów. Największa różnica między tym a sedpodejściem do rur polega na tym, że jest to znacznie bezpieczniejsze. Nadal jest łatwiejszy do odczytania niż regexowa zupa z odwrotnym ukośnikiem, pod warunkiem, że rozumiesz podstawowe konstrukcje skryptowe powłoki.
jw013
Masz rację; to jest znacznie czystsze. A $argzmienna znacznie ułatwia odczyt.
hobbes3
1
@DQdlM Fragment, który opublikowałem powyżej, po prostu findwywołuje shprzy użyciu dość przenośnej składni POSIX. Aby uzyskać więcej informacji, możesz sprawdzić sekcję Rozszerzanie parametrów , sekcję dotyczącą poleceń złożonych, która wyjaśnia forpętlę , oraz specyfikację POSIXfind . Oprócz tych zasobów chętnie odpowiem na wszelkie Twoje pytania.
jw013
6

W systemach Debian i Ubuntu mogę użyć rename 's/\(1\)//' *.m4arozwiązania problemu.

Dom
źródło
Dziwne, mogę to zrobić man rename, ale tak naprawdę nie mam rename: -bash: rename: command not found( which renamenic nie pokazuje).
hobbes3
@ hobbes3 na komputerze Mac tutaj man -a renameznajduje rename (2) - syscall, a rename (n) - polecenie TCL. Nie ma tam ani zmiany nazwy (1), ani samego narzędzia.
yrk
1
IIRC renameto skrypt perla dołączony do przykładów zawartych w niektórych instalacjach perla, a kilka dystrybucji zawiera go na ścieżce, ponieważ jest to wygodne polecenie. (@Hobbes być może można go znaleźć w Internecie)
Kevin
@ Kevin w moim systemie renamejest normalnym plikiem binarnym (nie perl). Kolejnym zastrzeżeniem jest to, że nie wszystkie wersje renamewyrażeń regularnych obsługują. Na przykład wersja, którą mam, pozwala tylko na bezpośrednią zamianę łańcucha, np.rename '(1)' '' *.m4a
Patrick
1
renameSkrypt Perla jest instalowany tylko przez Debian i pochodne. Inne systemy Linux mają inne renamenarzędzie (pokazane przez Patricka). OSX nie ma żadnego.
Gilles „SO- przestań być zły”
4

W Zsh , używając Zmv :

autoload zmv      # you can put this line in your .zshrc
zmv '(*)\(1\)(*)' '$1$2'

W drugim argumencie (nowa nazwa) $1i $2odwołaj się do grup (PATTERN)w nawiasach we wzorcu źródłowym. Innym sposobem napisania tej zmiany jest

zmv '(*)' '${1/\(1\)/}'
Gilles „SO- przestań być zły”
źródło
3

Poniższe podejście umożliwia podgląd / przycinanie wygenerowanych poleceń przed ich wykonaniem i jest bardzo przenośne: powinno działać nie tylko na Macu, nie tylko z bash, i nie tylko z GNU sed; nawet w systemach bez findpolecenia du(1) można bez problemu zastąpić go (1).

find . -name '*(1).m4a' |
sed 's/\(.*\)(1).m4a$/mv & \1.m4a/' 

Jeśli jesteś zadowolony z wydrukowanych poleceń, uruchom ponownie z | sh -xdołączonym.

Jeśli martwisz się spacjami w nazwach plików, dodaj kolejne s, aby uciec ze wszystkich spacji:

find . -name '*(1).m4a' |
sed -e 's/ /\\ /g' -e 's/\(.*\)(1).m4a$/mv & \1.m4a/'

Jeśli spodziewane są inne znaki specjalne, staje się to nieco trudniejsze:

find . -name '*(1).m4a' |
sed -e "s/'/'\\\\''/g" -e 's/\(.*\)(1).m4a$/mv '\''&'\'' '\''\1.m4a'\'/

Pierwsza funkcja konwertuje wszystko 'do postaci takiej, że są one brane dosłownie, gdy znajdują się w środku '...'łańcucha w kształcie ciągu. Druga funkcja generuje polecenia mv , których argumenty są zawarte w środku '...'.

szarpać
źródło
1
Polecenie to nie zmienia nazwy pliku (spacji (i wszystkich innych znaków specjalnych) ani nie dodaje cudzysłowów wokół nazwy pliku. Próbowałem zmodyfikować twoje polecenie, ale nie mogłem wymyślić, jak dodać cudzysłowy do obu nazw plików dla mvpolecenia. Jednym z wynikowych wyników twojego polecenia jest mv ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us(1).m4a ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us.m4a. A jeśli wykonam to polecenie, dostanę -bash: syntax error near unexpected token '('.
hobbes3
to ma być zadanie domowe :), ale ok, zaktualizuję odpowiedź
yrk
Nawiasem mówiąc, GNU sed może wykonać polecenie samodzielnie, dodając epolecenie po podstawieniu. Na przykład find . -name '*(1).m4a' | sed 's/\(.*\)(1).m4a$/mv & \1.m4a/e'wykona polecenie exexcute bez potokowania do sh -x.
pędzi
@ Rush to jedyny sed, który to robi; i musi uruchomić powłokę tak, ale nowy jeden dla każdego polecenia (przypomina dokonać za problem, nie?)
Yrk
2
Nie sądzę, że powinniśmy głosować z góry. W końcu jest to prawidłowe rozwiązanie.
hobbes3
1

Oto mały skrypt, który to robi:

for var in `find . -type f -name "*(1).m4a"`; do
    new=`echo $var | cut -d'(' -f1`;
    mv $var $new.m4a;
done
anupam
źródło
ten dusi się na plikach ze spacjami w nazwach; Wymaga to również wystarczająco duży tymczasowego magazynowania dla`find ...`
Yrk