Как переименовать большое количество файлов

У меня есть каталог с файлами как это

a.JPG
b.JPG
c.JPG

Я хотел бы выполнить что-то вроде этого

git mv a.JPG a.jpg

Я пытался использовать xargs и другие инструменты, но ничто, кажется, не работает.

8
задан Nick Vanderbilt 5 June 2010 в 17:03
поделиться

4 ответа

Суть решения заключается в использовании инструмента/метода, который автоматизирует массовое переименование. Вы можете использовать mv в сочетании с git add или просто git mv. В любом случае вам придется предпринять дополнительные шаги, если вы используете файловую систему, не чувствительную к регистру. Поэтому, прежде чем мы займемся массовым переименованием, будет полезно немного обсудить, как обрабатывается регистр.

Чувствительность к регистру

Некоторые системы (или комбинации система+файловая система, например, стандартный вариант файловой системы HFS+ в Mac OS X*) сохраняют регистр, но нечувствительны к регистру. В таких системах может потребоваться осторожность при переименовании, которое включает только изменение регистра имени. Обычным обходным решением является использование временного имени, отличающегося не только регистром, в качестве "моста" между двумя именами, отличающимися только регистром (например, mv foo.JPG tmp && mv tmp foo.jpg).

* В Mac OS X можно использовать файловые системы, чувствительные к регистру (включая чувствительный к регистру вариант HFS+).

С этого момента я буду считать, что файловая система не чувствительна к регистру.

Команда mv в Mac OS X может обрабатывать переименования только с учетом регистра за один шаг. Она выдаст запрос "перезаписать?", если будет запущена с опцией -i, и пропустит переименование, если будет задана опция -n. Это удается только благодаря тому, что многие части Unix-подобных систем работают по умолчанию по принципу "достаточно веревки, чтобы повеситься".

Команда git mv немного более параноидально относится к ситуации. Она отказывается выполнять операцию (ошибка "место назначения существует"), если не указать опцию -f/--force.

# this will succeed, though it may fail/prompt if mv is aliased to use -n/-i
mv foo.JPG foo.jpg

# this will succeed
mv -f bar.JPG bar.jpg

# this will succeed but give a warning
git mv -f quux.JPG quux.jpg

Опции Bulk Rename

Perl rename

Желаемая операция достаточно проста, чтобы выполнить ее с помощью небольшого сценария оболочки, но вы можете воспользоваться утилитой Perl rename (той, о которой упоминает Джордан Льюис), если вам нужно сделать что-то гораздо более сложное. Вы можете попробовать rename из пакета perl Debian, или, если вы готовы использовать CPAN, вы можете установить File::Rename, который включает в себя программу rename.

ksh, bash, zsh, dash

Используемый ниже -ef не совместим с POSIX. Аналогично, хотя -e определен в POSIX, он не совместим с чистым-Bourne. Тем не менее, оба они широко поддерживаются.

for f in *.JPG; do
    ff="${f%.JPG}.jpg"
    test -e "$f" || continue        # possible when not using nullglob
    test "$f" != "$ff" || continue  # possible when using nocaseglob
    if test -e "$ff" &&
       ! test "$f" -ef "$ff"; then  # possible on a case sensitive filesystem
        echo "skipping <$f>: destination <$ff> exists and is distinct" 1>&2
        continue
    fi

    # "mv" with "git rm" and "git add"
    mv -f "$f" "$ff"     &&
    git rm --cached "$f" &&
    git add "$ff"
done

Последний раздел (mv, git rm, git add) можно заменить просто git mv:

    # "git mv"
    git mv -f "$f" "$ff"

Если вы очень обеспокоены тем, что переименование может не сработать в системах, нечувствительных к регистру, то можно использовать временное имя:

    # temp-based "mv" with "git rm" and "git add"
    t="$ff.tmp"; while test -e "$t"; do t="$t.tmp"; done
    mv -n "$f" "$t"      &&
    mv -n "$t" "$ff"     &&
    git rm --cached "$f" &&
    git add "$ff"

Или с помощью git mv:

    # temp-based "git mv"
    t="$ff.tmp"; while test -e "$t"; do t="$t.tmp"; done
    git mv "$f" "$t"  &&
    git mv "$t" "$ff"

zsh/ zmv

Здесь нужно -f как для zmv, так и для git mv.

zsh -c 'autoload zmv && $0 $@' zmv -fp git -o 'mv -f' '(*).JPG' '$1 x.jpg'

Теперь, когда все они переименованы и обновлены в индексе Git'а, вы можете зафиксировать их.

Но смогут ли другие пользователи Git, использующие файловые системы, чувствительные к регистру, проверить их?

git checkout After a Case-Only Rename

Если есть другие пользователи вашей истории, у них, вероятно, всё ещё будут файлы JPG, и когда они в конце концов проверят (потомка) ваш коммит с файлами jpg. Что произойдёт для них?

Независимо от того, что произойдёт, нет необходимости в "rename to temp, commit, rename to final, commit". git checkout не применяет коммиты последовательно при переходе между коммитами. Он действительно работает путём "слияния" индекса и рабочего дерева из HEAD с новым коммитом. Это фактически означает, что он "перепрыгивает" непосредственно на новый коммит, перетаскивая при этом не противоречащие друг другу изменения, найденные между HEAD и индексом/рабочим деревом.

Внутри Git рассматривает переименования как удаление и добавление. Я не нашёл никакой документации, описывающей поведение git checkout в отношении порядка удаления и добавления, поэтому я обратился к исходному коду. git checkout обрабатывает все удаления перед любыми обновлениями/добавлениями (cmd_checkout -> switch_branches -> merge_working_tree (-> reset_tree) -> unpack_trees -> check_updates).

Вы можете проверить это сразу после фиксации переименования:

git checkout HEAD~ # note: detached HEAD
# verify that the original names are back in place
git checkout -     # back to your branch
# verify that the new names are in place again

git blame на файле, похоже, указывает на вероятную фиксацию: Make unpack-tree update removed files before any updated files, который был впервые выпущен в Git 1.5.6-rc0 (2008-06-18). Таким образом, хотя это и недокументировано(?), это поведение было реализовано специально для поддержки файловых систем, нечувствительных к регистру.

Спасибо, Линус!

9
ответ дан 5 December 2019 в 11:22
поделиться

Используйте стандартную утилиту Linux rename(1). После того как вы переименовали файлы, добавьте их в git.

2
ответ дан 5 December 2019 в 11:22
поделиться

Можно ли просто изменить регистр файла, зависит от вашей файловой системы. Даже если это работает в вашей файловой системе, вы можете создать проблемы для других людей, обновляющих файлы. Лучше всего будет переименовать их, зафиксировать, а затем переименовать обратно. Измените все на *.tmp с помощью следующего сценария bash:

for i in *.JPG; do mv $i ${i%.JPG}.tmp; done

Затем переместите их все в git. Вы можете использовать аналогичную команду, но я бы рекомендовал ознакомиться с guess-renames, который поможет с перемещением.

Затем переименуйте их все обратно в *.jpg с помощью аналогичного процесса.

3
ответ дан 5 December 2019 в 11:22
поделиться

Возможно, переименуйте их в * .somethingelse, а затем снова переименуйте в * .jpg

1
ответ дан 5 December 2019 в 11:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: