Рекурсивное переименование файлов
Имеется каталог со множеством подкаталогов, в каждом из которых находится файл «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-совместимый ответ на это
Любитель выдумывать? Про то, что для каждой задачи свой инструмент, не слышали?
Find multiple files and rename them in Linux
I am having files like a_dbg.txt, b_dbg.txt . in a Suse 10 system. I want to write a bash shell script which should rename these files by removing «_dbg» from them. Google suggested me to use rename command. So I executed the command rename _dbg.txt .txt *dbg* on the CURRENT_FOLDER My actual CURRENT_FOLDER contains the below files.
CURRENT_FOLDER/a_dbg.txt CURRENT_FOLDER/b_dbg.txt CURRENT_FOLDER/XX/c_dbg.txt CURRENT_FOLDER/YY/d_dbg.txt
CURRENT_FOLDER/a.txt CURRENT_FOLDER/b.txt CURRENT_FOLDER/XX/c_dbg.txt CURRENT_FOLDER/YY/d_dbg.txt
Its not doing recursively, how to make this command to rename files in all subdirectories. Like XX and YY I will be having so many subdirectories which name is unpredictable. And also my CURRENT_FOLDER will be having some other files also.
13 Answers 13
You can use find to find all matching files recursively:
find . -iname "*dbg*" -exec rename _dbg.txt .txt '<>' \;
EDIT: what the ‘<>‘ and \; are?
The -exec argument makes find execute rename for every matching file found. ‘<>‘ will be replaced with the path name of the file. The last token, \; is there only to mark the end of the exec expression.
All that is described nicely in the man page for find:
-exec utility [argument . ] ; True if the program named utility returns a zero value as its exit status. Optional arguments may be passed to the utility. The expression must be terminated by a semicolon (``;''). If you invoke find from a shell you may need to quote the semicolon if the shell would otherwise treat it as a control operator. If the string ``<>'' appears anywhere in the utility name or the argu- ments it is replaced by the pathname of the current file. Utility will be executed from the directory from which find was executed. Utility and arguments are not subject to the further expansion of shell patterns and constructs.
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.