Добавить префикс в виде символа подчеркивания ко всем каталогам в каталоге

Добавить префикс в виде символа подчеркивания ко всем каталогам в каталоге

У меня есть несколько каталогов внутри каталога, который мне нужно переименовать, включив в начало имени символ подчеркивания «_».

В качестве теста я создал родительский каталог, содержимое которого состоит из трех каталогов:

dir1, dir2 and dir3; three files: file1, file2, file3

и три файла с точечным префиксом:

.file1, .file2, .file3 

До сих пор я пробовал использовать gfind, после установки findutilsна Mac:

gfind . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh

Это возвращает:

mv: rename ./. to ./_.: Invalid argument

Итак, я попробовал использовать rename с find:

find . -type d -exec rename 's/^/_/' {} \;

Это возвращает:

Can't rename '.' to '_.': Invalid argument
Can't rename './dir1' to '_./dir1': No such file or directory
Can't rename './dir2' to '_./dir2': No such file or directory
Can't rename './dir3' to '_./dir3': No such file or directory

Может, у кого-нибудь есть действенное решение или кто-нибудь знает, где я могу ошибаться?

решение1

Если я правильно понял, у вас есть такие файлы и каталоги:

$ mkdir dir{1,2,3} ; touch file{1,2,3} .file{1,2,3}
$ ls -A
.file1  .file2  .file3  dir1  dir2  dir3  file1  file2  file3

и хотите переименовать каталоги в _dir1, _dir2, _dir3?

Это find/ printf mvвыглядит выполнимым. Давайте посмотрим, что он напечатает:

$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n"
mv "./." "./_."
mv "./dir2" "./_dir2"
mv "./dir3" "./_dir3"
mv "./dir1" "./_dir1"

Первая команда выдаст ошибку, так как "точка" является специальной и вы не можете ее переименовать. Но остальные должны быть в порядке и должны переименовать каталоги так, как вы хотели:

$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
mv: cannot move ‘./.’ to ‘./_.’: Device or resource busy
$ ls -d _dir*
_dir1  _dir2  _dir3

Однако передача команд в оболочку немного некрасива, и если имена файлов достаточно странные, результаты будут неожиданными (например, если они содержат $знаки, которые вызовут подстановку переменных или команд в оболочке).

Если все файлы находятся на одном уровне, то это должно сработать:

for x in * ; do [ -d "$x" ] && mv "$x" "_$x" ; done

(Хотя если _$xон уже существует, $xто будет перенесен туда.)

Если вы хотите включить каталоги, имена которых начинаются с точки, используйте shopt -s dotglobbeforehand.

Это тоже близко:

find . -type d -exec rename 's/^/_/' {} \;

Но поскольку findдает renameпути, начинающиеся с ./, нам нужно это учитывать. Только на одном уровне это должно сделать (изменяет лидирующий ./на ./_):

find . -type d -exec rename 's,^./,./_,' {} \;

Чтобы получить каталоги на всех уровнях, find -execdirможет быть проще всего использовать. Он запускает команду в каталоге файла. Нам нужно -depthобработать переименования в правильном порядке.

find . -depth -type d -execdir rename 's,^./,./_,' {} \;

Может быть, также добавить ! -name .. например

$ mkdir foo foo/dir1 foo/dir2
$ find . -depth \! -name . -type d -execdir rename 's,^./,./_,' {} \;
$ ls  _foo
_dir1  _dir2

решение2

Проблема с первой попыткой в ​​том, что вы пытаетесь переименовать и .саму папку (текущую папку), и все подпапки. Пропустите ее.

gfind . -mindepth 1 -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh

Но на самом деле не делайте этого. Никогда не передавайте данные в sh.Это не работает.Если только ваши имена файлов не содержат никаких специальных символов оболочки. Если у вас есть файл с именем `rm -r ~`(совершенно допустимое, хотя и необычное, имя файла), то pop переходит в ваш домашний каталог. Вызовите оболочку, -execкак я показываю ниже.

Ваша вторая попытка не сработала, потому что вы вставляете _в начало полного пути, вместо того чтобы добавить его в начало базового имени, т. е. после последнего /.

find . -mindepth 1 -type d -exec rename 's![^/]+$!_$&!' {} \;

Ни один из них на самом деле не работает, если у вас есть вложенные каталоги, потому что они переименовывают каталоги, пока findони проходят по ним. Когда вы делаете это, вам нужно действовать с содержимым каталога, прежде чем действовать с самим каталогом, с опцией -depth.

find . -mindepth 1 -depth -type d -exec rename 's![^/]+$!_$&!' {} \;

Более переносимо, вызовите оболочку и запустите mv. Это работает на любой системе POSIX.

find . -depth -type d -name . -o -exec sh -c '
    for d do mv "$d" "${d%/*}/_${d##*/}"; done
' sh {} +

Если у вас нет вложенных подкаталогов, то вы можете использовать простой цикл оболочки. Если вы не хотите переименовывать каталоги, имена которых начинаются с точки, просто используйте подстановочный знак */, который соответствует только каталогам и символическим ссылкам на каталоги.

for d in */; do
  if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi   # robustness in case there are no subdirectories
  if [ -L "$d" ]; then continue; fi     # optional: skip symbolic links to directories
  mv -- "$d" "_${d%/}"
done

Если вы хотите переименовать все каталоги, включая те, имена которых начинаются с точки, то .*также включите подстановочный знак.

for d in .*/ */; do
  if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi
  if [ -L "$d" ]; then continue; fi
  mv -- "$d" "_${d%/}"
done

Обратите внимание, что при перемещении fooв _foo, если уже существует каталог с именем _foo, старый каталог будет перемещен fooв _foo/foo. Чтобы защититься от этого, добавьте явную проверку.

Или, как вариант, используйте zsh. Он предустановлен на macOS. Сначала запустите autoload -U zmv(вставьте это в свой .zshrcили в свой скрипт), чтобы загрузитьzmvфункция, то нерекурсивная версия (включая файлы точек, исключая символические ссылки —  (/D)это наборквалификаторы glob):

zmv -Q '*(/D)' '_$f'

или рекурсивная версия (с использованиеммодификаторыдля извлечения имени каталога и базового имени):

zmv -Q '**/*(/D)' '$f:h/_$f:t'

zmvоткажется делать ход, если цель уже существует.

Связанный контент