Рекурсивное переименование файлов
Имеется каталог со множеством подкаталогов, в каждом из которых находится файл «readme». Нужно переименовать каждый файл «readme» в «readme_old». Как это сделать простой командой на bash?
find -type f -name 'readme' -exec rename "readme" "readme_old" '<>' \;
пример, как не осилить rename, но не подать вид
Ну не осилил… Только не я один. 🙂
find -type f -name 'readme' -execdir mv readme readme_old \;
Попытался выпендрится — не получилось. Если ты не в курсе, дружок, не у всех gnu find.
Пример, как не осилить, что rename несколько, но пытаться показать, что что-то знаешь.
Сработало! Огромнейшее СПАСИБО!
Если ты не в курсе, дружок, не у всех gnu find.
У кого нет gnu find, у того нет и rename из util-linux
Попытался выпендрится — не получилось.
А что если я шизофреник, и у меня именно так? А? А? А?
У кого нет gnu find, у того нет и rename из util-linux
Ну так то да, теоретически множество gnu мощнее множества linux-ов. Но это не важно, ибо задача то решается в рамках posix совершенно тупо:
$ find -name "readme" -type f -exec mv "<>" "<>"_old \;
vodz ★★★★★ ( 06.06.20 22:17:17 MSK )
Последнее исправление: vodz 06.06.20 22:18:53 MSK (всего исправлений: 1)
очень интересно содержимое этих readme, заинтриговал
Полезное добавление, спасибо.
Всё проще. readme это условное название. Требовалось только переименование одноимённых файлов.
ибо задача то решается … совершенно тупо
Думаешь, ТС осилит твой перл? Мой ответ соответствует его уровню понимания «простой команды», и он может осилить изменить его под свои нужды.
Изначально такой задачи не было. Эта надуманная проблема началась из-за «попытался выпендрится — не получилось». Я написал свой вариант без какой-либо (эмоциональной) оценки оппонента.
Никто никому здесь не соответствует.
Ответ на подобное всегда на ЛОРе был таким:
cat test. test. test. | perl -e .
Да успокойтесь вы… Вам же было сказано — «попытался выпендриться — не получилось». Что делает хороший человек, когда его просят помочь? Правильно, помогает. Что делает дебил недоучка, с неудовлетворёнными амбициями? Правильно -пытается показать, что он что то знает, но никому не скажет. Попросту выпендривается.
Чтобы не возникало вопросов, скажу «умникам» таки зачем мне это было нужно:
find -type f -name «.checksums» -exec rm -f <> ;
find -type f -name «.pkgfiles» -exec rm -f <> ;
find -type f -name ‘spkgbuild’ -execdir mv spkgbuild Pkgfile ;
find -type f -name Pkgfile -exec sed -i -r ‘s/#\ depends/#\ Depends\ on/g’ <> ;
Просто тупанул в одном месте и «сделал звонок товарищу». Всё, успокойтесь, любители делать гадости…
cdrw ( 07.06.20 15:01:33 MSK )
Последнее исправление: cdrw 07.06.20 15:13:42 MSK (всего исправлений: 2)
find -type f -name ‘spkgbuild’ -execdir mv spkgbuild Pkgfile ;
vodz , твой простой posix-совместимый ответ на это
Любитель выдумывать? Про то, что для каждой задачи свой инструмент, не слышали?
Recursively rename files (change extension) in Linux
How do I rename all files in a directory, recursively, changing one file extension to another, for thousands of files in thousands of subfolders? I see a lot of commands that do almost what I want, but not quite.
find . -name "*.andnav" -exec rename .andnav .tile <> \; syntax error at (eval 1) line 1, near "."
rename -nv 's/\.andnav$/\.tile/i' *.andnav 0.png.andnav renamed as 0.png.tile
7 Answers 7
find . -name "*.andnav" -exec rename -v 's/\.andnav$/\.tile/i' <> \; ./0/0.png.andnav renamed as ./0/0.png.tile ./0/1.png.andnav renamed as ./0/1.png.tile ./1/0.png.andnav renamed as ./1/0.png.tile ./1/1.png.andnav renamed as ./1/1.png.tile
of course remove the -v when actually doing it, or it will waste time displaying all the files
autoload zmv zmv -n '(**/)(*).andnav' '$1$2.tile'
Remove the -n to actually perform the renaming.
find . -name '*.andnav' -exec sh -c 'mv "$0" "$.tile"' <> \;
Explanation
The above starts walking the directory tree starting at the current working directory ( . ). Every time a file name matches the pattern *.andnav (e.g., foo.andnav ) the following command is executed:
Where $0 is foo.andnav and $.tile replaces the .andnav suffix with .tile so basically:
I just meant an explanation of the syntax and behaviour- people (like me!) might not be familiar with bash parameter expansion
I found this method is easier and easier to read:
find . -name "*.andnav" | rename "s/\.andnav$/.tile/"
At least on Ubuntu derivations rename takes a list of files from STDIN if none are on the command line. And this can be tested easily with:
find . -name "*.andnav" | rename -vn "s/\.andnav$/.tile/"
find -execdir rename
https://superuser.com/a/213146/128124 works directly only for suffixes, but this will work for arbitrary regex replacements on basenames:
PATH=/usr/bin find . -depth -execdir rename 's/_dbg.txt$/_.txt' '<>' \;
PATH=/usr/bin find . -type f -execdir rename 's/_dbg.txt$/_.txt' '<>' \;
-execdir first cd s into the directory before executing only on the basename.
Tested on Ubuntu 20.04, find 4.7.0, rename 1.10.
Convenient and safer helper for it
find-rename-regex() ( set -eu find_and_replace="$1" PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \ find . -depth -execdir rename "$" "s/$" '<>' \; )
Sample usage to replace spaces ‘ ‘ with hyphens ‘-‘.
Dry run that shows what would be renamed to what without actually doing it:
Command explanation
The awesome -execdir option does a cd into the directory before executing the rename command, unlike -exec .
-depth ensure that the renaming happens first on children, and then on parents, to prevent potential problems with missing parent directories.
-execdir is required because rename does not play well with non-basename input paths, e.g. the following fails:
rename 's/findme/replaceme/g' acc/acc
The PATH hacking is required because -execdir has one very annoying drawback: find is extremely opinionated and refuses to do anything with -execdir if you have any relative paths in your PATH environment variable, e.g. ./node_modules/.bin , failing with:
find: The relative path ‘./node_modules/.bin’ is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove that entry from $PATH
-execdir is a GNU find extension to POSIX. rename is Perl based and comes from the rename package.
Rename lookahead workaround
If your input paths don’t come from find , or if you’ve had enough of the relative path annoyance, we can use some Perl lookahead to safely rename directories as in:
git ls-files | sort -r | xargs rename 's/findme(. *\/)\/?$/replaceme/g' '<>'
I haven’t found a convenient analogue for -execdir with xargs : Xargs: change working directory to file path before executing?
The sort -r is required to ensure that files come after their respective directories, since longer paths come after shorter ones with the same prefix.