Odpowiedzi:
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) rename
w 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
${1%.abc}...
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
.
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/'
rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abc
w nowoczesnej wersji Bash.
s
na początku i jakie inne opcje mogę tam wykorzystać. Dzięki !
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 -execdir
działania, aby rozwiązać ten problem. Jeśli użyjesz -execdir
zamiast -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 -execdir
wstawi znalezioną ścieżkę. Pojedyncze cudzysłowy są wymagane, aby umożliwić obsługę nazw plików ze spacjami i znakami powłoki.-type
i -name
są 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.-execdir
Wymagany 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
$1
odwoł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..abc
nowa nazwa pliku zakończy się na .abc. W sekcji „zamień” nie ma potrzeby ucieczki od metaznaku kropkowego/
koniec wyrażenia regularnegoPrzed
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: rename
Opcja -n jest przydatna. Robi próbę na sucho i pokazuje, jakie nazwy zmieni, ale nie wprowadza żadnych zmian.
--execdir
nie przeszedł on na początek, ./
więc \.\/
część wyrażenia regularnego można pominąć. Może dlatego, że korzystam z OSX, a nie Ubuntu
Inny przenośny sposób:
find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;
# Rename all *.txt to *.text
for f in *.txt; do
mv -- "$f" "${f%.txt}.text"
done
Zobacz także wpis, dlaczego nie powinieneś analizować ls
.
Edycja: jeśli musisz użyć basename, składnia wyglądałaby następująco:
for f in *.txt; do
mv -- "$f" "$(basename "$f" .txt).text"
done
https://unix.stackexchange.com/questions/19654/changing-extension-to-multiple-files
To właśnie zrobiłem i działałem tak, jak chciałem. Użyłem mv
polecenia. Miałem wiele .3gp
plikó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
file.3gp
na file.3gp.mp4
, co może nie być tym, czego chce większość ludzi.
.3gp
lub .mp4
tutaj były tylko w celach ilustracyjnych.
basename "$i" .mp4
poprzedniego rozszerzenia zamiast „ren- $ i.mp4”.
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 #1
na wymianę. Do *
odpowiada #2
. Byłaby to wersja nierekurencyjna
mmv '*.abc' '#1.edefg'
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 -execdir
opcja robi cd
w katalogu a przed wykonaniem rename
polecenia, 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
PATH
Hacking jest wymagane, ponieważ -execdir
ma jeden bardzo przykry wadę: find
jest bardzo uparty i nie chce nic robić ze -execdir
jeśli masz żadnych ścieżek względnych w PATH
zmiennej ś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?
-execdir
to rozszerzenie GNU do POSIX . rename
jest oparty na Perlu i pochodzi z rename
pakietu. Testowane w Ubuntu 18.10.
Zmień nazwę obejścia obejścia
Jeśli twoje ścieżki wejściowe nie pochodzą find
lub 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 -execdir
z xargs
: https://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686
Jest sort -r
to 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.