Jak zmienić rekursywnie rozszerzenie wielu plików z wiersza poleceń?

Odpowiedzi:

85

Przenośny sposób (który działa na każdym systemie zgodnym z POSIX):

find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;

W bash4 możesz użyć globstar, aby uzyskać rekurencyjne globusy (**):

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done

Komenda (perl) renamew Ubuntu może zmieniać nazwy plików przy użyciu składni wyrażeń regularnych perl, którą można łączyć z globstar lub find:

# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)  

# Best to process the files in chunks to avoid exceeding the maximum argument 
# length. 100 at a time is probably good enough. 
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
  rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done

# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +

Zobacz także http://mywiki.wooledge.org/BashFAQ/030

geirha
źródło
pierwszy po prostu dołącza nowe rozszerzenie do starego, nie zastępuje ...
user2757729
3
@ user2757729, to go zastępuje. "${1%.abc}"rozwija się do wartości $1oprócz „bez” .abcna końcu. Zobacz faq 100, aby uzyskać więcej informacji na temat manipulacji ciągami powłoki.
geirha
Uruchomiłem to na strukturze plików ~ 70k ... przynajmniej na mojej powłoce zdecydowanie to nie zastąpiło.
user2757729
1
@ user2757729 Prawdopodobnie opuściłeś „abc” w${1%.abc}...
kvnn
1
W przypadku, gdy ktoś zastanawia się, co robi znak podkreślenia w pierwszym poleceniu, jest to konieczne, ponieważ z sh -c pierwszym argumentem przypisuje się $ 0, a wszelkie pozostałe argumenty są przypisywane do parametrów pozycyjnych . Więc _jest obojętne argumentem, a „{}” jest pierwszym argumentem pozycyjnym sh -c.
hellobenallan
48

Wykona to wymagane zadanie, jeśli wszystkie pliki znajdują się w tym samym folderze

rename 's/.abc$/.edefg/' *.abc

Aby zmienić nazwę plików rekurencyjnie, użyj tego:

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'
Czakra
źródło
8
Lub rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abcw nowoczesnej wersji Bash.
Adam Byrtek,
Wielkie dzięki, Adam, że dał mi wskazówkę, jak rekurencyjnie używać * .abc w folderach!
Rafał Cieślak,
Świetna wskazówka, dzięki! Gdzie mogę znaleźć więcej dokumentacji na temat małego fragmentu składni wyrażenia regularnego? Jak sna początku i jakie inne opcje mogę tam wykorzystać. Dzięki !
Anto
@AdamByrtek Właśnie nie udało mi się z twoją sugestią na temat Utopic. („żadnych takich plików” lub niektórych takich.)
Jonathan Y.
14

Jednym z problemów z rekurencyjnymi nazwami jest to, że bez względu na metodę, której używasz do zlokalizowania plików, przekazuje całą ścieżkę rename, a nie tylko nazwę pliku. Utrudnia to skomplikowane zmiany nazw w zagnieżdżonych folderach.

Używam find„s -execdirdziałania, aby rozwiązać ten problem. Jeśli użyjesz -execdirzamiast -exec, określone polecenie jest uruchamiane z podkatalogu zawierającego dopasowany plik. Zamiast przejść całą ścieżkę rename, przechodzi ona tylko ./filename. To znacznie ułatwia napisanie wyrażenia regularnego.

find /the/path -type f \
               -name '*.abc' \
               -execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;

Szczegółowo:

  • -type f oznacza tylko wyszukiwanie plików, a nie katalogów
  • -name '*.abc' oznacza tylko pasujące nazwy plików, które kończą się na .abc
  • '{}'to symbol zastępczy, który oznacza miejsce, w którym -execdirwstawi znalezioną ścieżkę. Pojedyncze cudzysłowy są wymagane, aby umożliwić obsługę nazw plików ze spacjami i znakami powłoki.
  • Odwrotne ukośniki po -typei -namesą znakiem kontynuacji linii bash. Używam ich, aby ten przykład był bardziej czytelny, ale nie są one potrzebne, jeśli umieścisz wszystkie polecenia w jednym wierszu.
  • -execdirWymagany jest jednak odwrotny ukośnik na końcu linii. Jest tam, aby uciec od średnika, który kończy uruchamiane polecenie -execdir. Zabawa!

Objaśnienie wyrażenia regularnego:

  • s/ początek wyrażenia regularnego
  • \.\/dopasuj wiodące ./, które przechodzi -execdir. Użyj \, aby wyjść z. i / metaznaki (uwaga: ta część różni się w zależności od wersji find. Zobacz komentarz od użytkownika @apollo)
  • (.+)dopasuj nazwę pliku. Nawiasy przechwytują dopasowanie do późniejszego wykorzystania
  • \.abc ucieczka kropka, dopasuj abc
  • $ zakotwicz dopasowanie na końcu łańcucha

  • / oznacza koniec części „dopasowującej” wyrażenia regularnego i początek części „zamień”

  • version1_ dodaj ten tekst do każdej nazwy pliku

  • $1odwołuje się do istniejącej nazwy pliku, ponieważ przechwyciliśmy ją w nawiasach. Jeśli używasz wielu zestawów nawiasów w części „Dopasuj”, możesz odwołać się do nich tutaj, używając 2 USD, 3 USD itp.
  • .abcnowa nazwa pliku zakończy się na .abc. W sekcji „zamień” nie ma potrzeby ucieczki od metaznaku kropkowego
  • / koniec wyrażenia regularnego

Przed

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
    `-- nested_file.abc

Po

tree --charset=ascii

|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
    `-- version1_nested_file.abc

Wskazówka: renameOpcja -n jest przydatna. Robi próbę na sucho i pokazuje, jakie nazwy zmieni, ale nie wprowadza żadnych zmian.

Christian Long
źródło
Dzięki za wyjaśnienie szczegółów, ale co dziwne, kiedy próbuję tego, --execdirnie przeszedł on na początek, ./więc \.\/część wyrażenia regularnego można pominąć. Może dlatego, że korzystam z OSX, a nie Ubuntu
apollo
5

Inny przenośny sposób:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;
Neutrino
źródło
4
Witamy w Ask Ubuntu! Chociaż jest to cenna odpowiedź, zalecam jej rozwinięcie (edycję) w celu wyjaśnienia, w jaki sposób i dlaczego to polecenie działa.
Eliah Kagan
0

To właśnie zrobiłem i działałem tak, jak chciałem. Użyłem mvpolecenia. Miałem wiele .3gpplików i chciałem zmienić ich nazwy na wszystkie.mp4

Oto krótki oneliner:

for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done

Który po prostu skanuje bieżący katalog, pobiera wszystkie pliki .3gp, a następnie zmienia nazwy (przy użyciu mv) na ren-name_of_file.mp4

KhoPhi
źródło
To zmieni nazwę file.3gpna file.3gp.mp4, co może nie być tym, czego chce większość ludzi.
muru
@muru, ale zasada wciąż istnieje. Po prostu wybierz dowolne rozszerzenie, a następnie określ rozszerzenie docelowe, aby zmienić ich nazwę. Te .3gplub .mp4tutaj były tylko w celach ilustracyjnych.
KhoPhi,
Składnia jest preferowana, jednoliniowa i łatwa do zrozumienia. Chciałbym jednak użyć basename "$i" .mp4poprzedniego rozszerzenia zamiast „ren- $ i.mp4”.
TaoPR
Prawdziwe. Słuszna uwaga.
KhoPhi
0

Znalazłem łatwy sposób na osiągnięcie tego. Aby zmienić rozszerzenia wielu plików z jpg na pdf, użyj:

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done
Spokojna
źródło
0

Chciałbym użyć polecenia MMV z pakietu o tej samej nazwie :

mmv ';*.abc' '#1#2.edefg'

W ;dopasowuje zero lub więcej */i odpowiada #1na wymianę. Do *odpowiada #2. Byłaby to wersja nierekurencyjna

mmv '*.abc' '#1.edefg'
MvG
źródło
0

Zmień nazwy plików i katalogów za pomocą find -execdir | rename

Jeśli zamierzasz zmienić nazwę zarówno plików, jak i katalogów, nie tylko za pomocą przyrostka, to jest to dobry wzorzec:

PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
  find . -depth -execdir rename 's/findme/replaceme/' '{}' \;

Niesamowita -execdiropcja robi cdw katalogu a przed wykonaniem renamepolecenia, w przeciwieństwie do -exec.

-depth upewnij się, że zmiana nazwy nastąpi najpierw dla dzieci, a następnie dla rodziców, aby zapobiec potencjalnym problemom z brakującymi katalogami rodziców.

-execdir jest wymagane, ponieważ zmiana nazwy nie działa dobrze w przypadku ścieżek wejściowych innych niż basename, np. nie działa:

rename 's/findme/replaceme/g' acc/acc

PATHHacking jest wymagane, ponieważ -execdirma jeden bardzo przykry wadę: findjest bardzo uparty i nie chce nic robić ze -execdirjeśli masz żadnych ścieżek względnych w PATHzmiennej środowiskowej, na przykład ./node_modules/.bin, w przypadku braku z:

find: ścieżka względna „./node_modules/.bin” jest zawarta w zmiennej środowiskowej PATH, która nie jest bezpieczna w połączeniu z działaniem -execdir funkcji find. Usuń ten wpis z $ PATH

Zobacz także: Dlaczego użycie akcji „-execdir” nie jest bezpieczne dla katalogu znajdującego się w zmiennej PATH?

-execdirto rozszerzenie GNU do POSIX . renamejest oparty na Perlu i pochodzi z renamepakietu. Testowane w Ubuntu 18.10.

Zmień nazwę obejścia obejścia

Jeśli twoje ścieżki wejściowe nie pochodzą findlub jeśli masz dość relatywnej irytacji ścieżki, możemy użyć Perla lookahead, aby bezpiecznie zmienić nazwy katalogów jak w:

git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'

Nie znalazłem dogodnym dla analogowych -execdirz xargs: https://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686

Jest sort -rto konieczne, aby upewnić się, że pliki następują po odpowiednich katalogach, ponieważ dłuższe ścieżki pojawiają się po krótszych z tym samym prefiksem.

Ciro Santilli
źródło