2012-04-02 92 views
20

我想重命名我的應用程序中的許多文件,並需要能夠從app根通過git(即git mv%filenamematch%%replacement%)在所有子目錄中重命名替換匹配的文本。儘管如此,我並不擅長bash腳本。git重命名許多文件和文件夾

更新:如果也重命名的目錄也匹配,這將是一件好事!

+0

你有一個例子嗎?什麼'%filenamematch%'喜歡;它總是在開始還是結束還是中間還是什麼? %替換%是什麼樣的? – GoZoner 2012-04-03 03:44:38

+0

它可以person.rb或lookitisa_person_here.html。在這兩種情況下,這個人都需要進行匹配,並從那個人轉到其他人。它也應該是區分大小寫的 – Jonathan 2012-04-03 11:44:27

回答

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%/') 

遵循什麼該做,你需要理解與管道「|」並用「$(...)」替換命令。這些強大的shell構造允許我們結合幾個命令來獲得我們需要的結果。見PipelinesCommand Substitution

這裏是發生了什麼事在這一個班輪:

  1. git ls-files:這產生了Git倉庫的文件列表。它類似於你從ls得到的結果,除了它只輸出Git項目文件外。從這個列表開始,確保您的.git /目錄中的任何內容都不會被觸及。

  2. | grep %filenamematch%:我們從git ls-files獲取列表並通過grep將其過濾爲僅包含我們要查找的單詞或模式的文件名。

  3. | sed -e 's/\(%filenamematch%[^/]*\).*/\1/':我們管通過的sed的S(替補)命令砍掉我們的匹配目錄之後的任何/後續字符(如果它正好是一個)sed(流編輯器),執行(-e)這些比賽。

  4. | uniq:在匹配是目錄的情況下,現在我們已經截斷了包含的目錄和文件,可能有很多匹配的行。我們使用uniq將它們全部合併爲一行。

  5. for file in ...:shell的"for" command將迭代列表中的所有項目(文件名)。每個文件名依次分配給變量「$ file」,然後在分號(;)後執行該命令。

  6. sed -e 's/%filenamematch%/%replacement%/':我們使用echo通過sed使用它的替換命令來管道每個文件名 - 這次是對文件名執行模式替換。

  7. git mv:我們使用這個git命令將現有文件($文件)mv到新文件名(由sed修改的文件名)。

更好地理解這一點的一種方法是單獨觀察這些步驟中的每一個。爲此,請在shell中運行以下命令,並觀察輸出。所有這些都是非破壞性的,你只觀察名單產生:

  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 2012-04-03 11:55:24

+1

當然。這是那些可能看起來有點令人生畏的bash單線之一。也許其他人會提出更簡潔或可讀的建議。我會添加一些解釋性說明。 – 2012-04-03 13:37:03

+1

注意:該命令確實有缺陷:如果您有任何與替換模式匹配的目錄,它們將導致錯誤。換句話說,它會嘗試使用'git mv path/person/file.rb path/alien/file.rb'進行移動,如果目標目錄不存在,這可能會失敗。 爲了解決這個問題,我們可以爲我們正在搜索的模式添加一點邏輯,以確保匹配的字符串後面沒有斜線。 – 2012-04-03 14:41:20

4

git mv裏面的一個shell循環?

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 

這裏是一個較長的例子(在bash或類似)

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''。如果您需要更清晰,請使用一些示例重命名來更新您的問題。 – 2012-04-02 22:35:36

+0

更新後的帖子也許現在更清楚,有什麼想法?我對bash腳本不太好。 – Jonathan 2012-04-03 00:13:32

+1

我希望擴展示例有所幫助! – 2012-04-03 14:50:02

0

我通常使用NetBeans來完成這種類型的工作,因爲當有更簡單的方法時,我避免使用命令行。 NetBeans對git有一些支持,您可以通過「收藏夾」選項卡在任意目錄/文件上使用它。

12

遲到了,但是,這應該在BASH工作(文件和目錄,但我很小心,對於目錄):

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

的描述使其工作,如果你解釋了之前和之後的地方在腳本中。在'* foo *'和'/ foo/bar'之間foo = before bar = after是我的假設 – Jonathan 2012-04-03 16:23:43

+2

是的。之前是'foo'(你之前評論中的人)。之後是'酒吧'。 BASH shell語法$ {variable/pattern-to-match/replace-with}用於生成新名稱。 – GoZoner 2012-04-03 17:36:58

+1

我在Ubuntu 14.04上得到'sh:1:Bad substitution',除非我用'bash'替換'sh',參見[這裏](https://gist.github.com/dukedave/2de1c6a93548bcaffd8a)。 – dukedave 2015-08-15 00:36:11

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文件?歷史是否保存? – 2017-01-12 21:16:48

+0

是的,它是一樣的。 'git mv'總是和'git rm'和'git add'一樣,Git不會像其他版本控制系統那樣存儲額外的元數據。 – Flimm 2017-01-16 08:54:11

+0

好的方法和容易理解。不過,對我來說,這是'重命名舊的新*',其中每個文件名('*')中第一次出現的'new'被替換爲'old'。 – 2017-03-31 07:34:41

0

我解決了這個爲自己通過使用 https://github.com/75lb/renamer - 完美工作:-) 沒有明確做一個git mv,但git似乎完美地處理結果。

當我跟着頂端回答我被困,在最後步驟的所有步驟,當我的控制檯

for pipe quote>

回答如果任何人都可以解釋這一點,我將不勝感激!

+0

這看起來像是提示關閉')'或'\''字符 – 2014-11-03 21:06:40

0

這很適合於我的使用情況:

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

說明:

  • ls folder*/*.css - 使用ls得到相匹配的glob模式是CSS文件中的所有目錄的列表。 (目錄開始folder和包含文件與.css擴展)
  • while read line - 就行由行輸出執行git mv$line變量包含 - 在所得的ls命令行接一行
  • do git mv -- $line ${line%.css}.css輸出讀每一行),而每個匹配文件名開頭的和不包括.css分機(帶有${line%和添加新.scss延伸(--用於防止文件名和標記之間的模糊)

下面的代碼可用於「空轉」(實際上不會執行git mv):

ls variant*/*.css | while read line; do echo git mv $line to ${line%.css}.scss; done;