2012-04-02 2 views
20

Я пытаюсь переименовать много файлов в моем приложении и должен иметь возможность сделать переименование во всех подкаталогах из корня приложения через git (т. Е. Git mv% filenamematch%% replacement%), который только заменяет соответствующий текст. Однако я не очень хорошо разбираюсь в сценариях bash.git переименовать много файлов и папок

обновление: было бы хорошо, если бы также переименованы каталоги, которые также совпадают!

+0

Есть ли у вас пример? Что такое% filenamematch%; это всегда в начале, конце или середине или что? Как выглядит %%%%? – GoZoner

+0

Это может person.rb или lookitisa_person_here.html. В обоих случаях человеку нужно сопоставить и изменить его на что-то другое. Также должно быть чувствительно к регистру – Jonathan

ответ

23

Это должно сделать трюк:

for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); git mv $file $(echo $file | sed -e 's/%filenamematch%/%replacement%/') 

следовать тому, что это делает, вы должны понимать, трубопровод с «|» и подстановку команд с помощью «$ (...)». Эти мощные конструкции оболочки позволяют нам комбинировать несколько команд, чтобы получить нужный нам результат. См. Pipelines и Command Substitution.

Вот что происходит в этом однострочника:

  1. git ls-files: Это создает список файлов в хранилище Git. Это похоже на то, что вы можете получить от ls, за исключением того, что он выводит только файлы проекта Git. Исход из этого списка гарантирует, что ничего в вашем каталоге .git/не коснется.

  2. | grep %filenamematch%: Мы берем список от git ls-files и прокладываем его через grep, чтобы отфильтровать его только на имена файлов, содержащие слово или образец, который мы ищем.

  3. | sed -e 's/\(%filenamematch%[^/]*\).*/\1/': Мы труба этих матчи через sed (редактор потока), исполняющий (-е) СЕПГ команды s (заменить) отрубить любые/и последующие символы после нашего каталога соответствия (если это случается один) ,

  4. | uniq: В случаях, когда совпадение является каталогом, теперь, когда мы отрубили содержащиеся в нем каталоги и файлы, может быть много совпадающих строк. Мы используем uniq, чтобы сделать их всех в одну строку.

  5. for file in ...: Оболочка "for" command будет перебирать все элементы (имена файлов) в списке. Каждое имя файла, в свою очередь, присваивает переменной «$ file», а затем выполняет команду после точки с запятой (;).

  6. sed -e 's/%filenamematch%/%replacement%/': Мы используем echo для соединения каждого имени файла через sed, используя его заменяющую команду снова - на этот раз, чтобы выполнить нашу замену шаблона на имя файла.

  7. git mv: Мы используем эту команду git для mv существующего файла ($ file) для нового имени файла (тот, который был изменен sed).

Одним из способов понять это лучше было бы наблюдать каждый из этих шагов в изоляции. Для этого запустите команды ниже в своей оболочке и посмотрите результат. Все это неразрушающие, только производя списки для вашего наблюдения:

  1. git ls-files

  2. git ls-files | grep %filenamematch%

  3. git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/'

  4. git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq

  5. for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); echo $file

  6. for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); echo $file | sed -e 's/%filenamematch%/%replacement%/'

+0

Не могли бы вы объяснить, что это делает? Я не понимаю. Кроме того, я обновил свой комментарий по моему вопросу, совпадение имени файла может быть частью имени файла, которое нужно изменить, поскольку это может быть some_person.html или person.rb, в обоих случаях должна быть сопоставлена ​​и изменена только часть «человек» – Jonathan

+1

Конечно. Это один из тех, которые могут выглядеть немного сложнее. Возможно, у других были бы предложения по его более кратким или читабельным. Я добавлю некоторые пояснительные примечания. –

+1

Примечание: эта команда имеет недостаток: если у вас есть каталоги, соответствующие вашему шаблону замены, они вызовут ошибку. Другими словами, он попытается выполнить перемещение с помощью 'git mv path/person/file.rb path/alien/file.rb', который, вероятно, завершится неудачно, если целевой каталог не существует. Чтобы исправить это, мы можем добавить небольшую логику к шаблонам, которые мы ищем, чтобы гарантировать, что строка соответствия не имеет следов после нее. –

4

git mv внутри оболочки петли?

What's the purpose of git-mv?

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

Опираясь на @ Джонатан-camenish: # (может кто-то изменить это в реальной ссылке?)

# things between backticks are 'subshell' commands. I like the $() spelling over `` 
# git ls-files  -> lists the files tracked by git, one per line 
# | grep somestring -> pipes (i.e., "|") that list through a filter 
#  '|' connects the output of one command to the input of the next 
# leading to: for file in some_filtered_list 
# git mv f1 f2 -> renames the file, and informs git of the move. 
# here 'f2' is constructed as the result of a subshell command 
#  based on the sed command you listed earlier. 

for file in `git ls-files | grep filenamematch`; do git mv $file `echo $file | sed -e 's/%filenamematch%/%replacement%/'`; done 

Вот уже пример (в Баш или аналогичный)

mkdir blah; cd blah; 
touch old_{f1,f2,f3,f4} same_{f1,f2,f3} 
git init && git add old_* same_* && git commit -m "first commit" 
for file in $(git ls-files | grep old); do git mv $file $(echo $file | sed -e 's/old/new/'); done 
git status 
# On branch master 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
# renamed: old_f1 -> new_f1 
# renamed: old_f2 -> new_f2 
# renamed: old_f3 -> new_f3 
# renamed: old_f4 -> new_f4 
# 

: http://en.wikibooks.org/wiki/Ad_Hoc_Data_Analysis_From_The_Unix_Command_Line/

+0

, где бы вы ни использовали '' mv'', используйте '' git mv''. Если вам нужна дополнительная ясность, обновите свой вопрос с помощью некоторых примеров переименований. –

+0

Обновлено сообщение, возможно, теперь более ясно, какие-то идеи? Я плохо разбираюсь в сценариях bash. – Jonathan

+1

Надеюсь, что расширенный пример поможет! –

0

Обычно я использую NetBeans для создания такого типа вещей, потому что я избегаю командной строки, когда есть более простой способ. NetBeans поддерживает некоторую поддержку git, и вы можете использовать его в произвольном каталоге/файле через вкладку «Избранное».

12

Late к партии, но это должно работать в BASH (для файлов и каталогов, но я был бы осторожен в отношении каталогов):

find . -name '*foo*' -exec bash -c 'file={}; git mv $file ${file/foo/bar}' \; 
+0

Было бы здорово, если бы вы объяснили, где до и после в скрипте. Между '* foo *' и '/ foo/bar' foo = before bar = после моего предположения – Jonathan

+2

Да. Before is 'foo' (человек в вашем предыдущем комментарии). После этого «бар». Синтаксис оболочки BASH $ {variable/pattern-to-match/replace-with} используется для генерации нового имени. – GoZoner

+1

Я получаю 'sh: 1: Bad substitution' на Ubuntu 14.04, если я не заменил' sh' на 'bash', см. [Здесь] (https://gist.github.com/dukedave/2de1c6a93548bcaffd8a). – dukedave

8

Переименование файлы с регулярным выражением с использованием команды rename:

rename 's/old/new/' * 

Затем зарегистрировать изменения с Git путем добавления новых файлов и удаления старых:

git add . 
git ls-files -z --deleted | xargs -0 git rm 

В новых версиях Git можно использовать --all флаг вместо:

git add --all . 
+0

Это то же самое, что ' git mv'ing файл? Сохранена ли история? –

+0

Да, это то же самое. 'git mv' всегда совпадает с' git rm' и 'git add' вместе, Git не сохраняет дополнительные метаданные для перемещений, как это делают другие системы управления версиями. – Flimm

+0

Хороший подход и достаточно легкий для понимания. Однако для меня это было «переименовать старый новый», где первое вхождение «new» в каждом имени файла ('*') заменяется на «old». –

0

Я решил это для себя используя https://github.com/75lb/renamer - отлично работал :-) Не делает явно git mv, но git, похоже, все равно имел дело с результатами.

Когда я все шаги в верхней ответ я застрял на заключительном этапе, когда моя консоль ответил

for pipe quote>

Если кто-нибудь может объяснить это я ценю это!

+0

Похоже, что это вызывает закрытие символа ')' или '\' –

0

Это хорошо работало для моего случая использования:

ls folder*/*.css | while read line; do git mv -- $line ${line%.css}.scss; done; 

Объяснение:

  • ls folder*/*.css - Использует ls, чтобы получить список всех каталогов с CSS файлов, которые соответствуют шаблону Глоб. (каталоги, начинающиеся с folder и содержащие файлы с .css расширениями)
  • while read line - Чтение в результате выхода из ls командной строки за строкой
  • do git mv -- $line ${line%.css}.css - Выполнить git mv на выходе линии за линией ($line переменная содержит каждая строка), подбирая начало каждого файла и исключая .css расширения (с ${line% и добавлением нового .scss расширения (-- используются для предотвращения неоднозначности между именами файлов и флагами)

код ниже, может быть использован для «сухого хода» (на самом деле не выполнить git mv):

ls variant*/*.css | while read line; do echo git mv $line to ${line%.css}.scss; done; 
Смежные вопросы