2017-02-09 184 views
2

我試圖使用PowerShell批量複製和重命名文件。使用PowerShell批量複製和重命名文件

原始文件被命名爲AAA001A.jpg,AAB002A.jpg,AAB003A.jpg等

我想這些文件使用新名稱複製,從文件名剝離前四個字符,並在這段時間之前的字符,以便複製的文件被命名爲01.jpg,02.jpg,03.jpg等。

我在Linux上有Bash腳本的經驗,但我很難過如何做到這一點與PowerShell。

Get-ChildItem AAB002A.jpg | foreach { copy-item $_ "$_.name.replace ("AAB","")" } 

(它不工作)

+0

您可以從顏色看在你的' 「$ _ name.replace(」 AAB 「 」「)」',它不是明智的處理,它會需要更像'$ _。name.replace(「AAB」,「」)' - 不在內部字符串引號內,或者「$ {$ _。name.replace(」AAB「,」「)}」'裏面字符串引號。 – TessellatingHeckler

+1

@TessellatingHeckler:除了您在雙引號字符串內而不是'$ {...}'的意思是'$(...)'(子表達式運算符)以外,很好的一點。 – mklement0

回答

0

注:
*雖然可能略超過abelenky's answer比較複雜,(a)是它更加堅固確保只處理符合所需模式的*.jpg文件,(b)顯示一些高級正則表達式技術,(c)提供背景信息並用OP方法解釋問題。
*本答案使用PSv3 +語法。

Get-ChildItem *.jpg | 
    Where-Object Name -match '^.{4}(.+).\.(.+)$' | 
    Copy-Item -Destination { $Matches.1 + '.' + $Matches.2 } -WhatIf 

保留命令短,目標目錄中並沒有明確的控制,所以副本將被放置在當前目錄。確保放置在相同的目錄中。作爲輸入文件,使用
Join-Path $_.PSParentPath ($Matches.1 + '.' + $Matches.2)內部{ ... }

-WhatIf預覽哪些文件會被複制到;將其刪除以執行實際的複製。

  • Get-ChildItem *.jpg輸出所有*.jpg文件 - 無論他們是否適合文件的方式進行重命名。

  • Where-Object Name -match '^.{4}(.*).\.(.+)$'然後縮小了匹配向下那些符合該圖案,使用正則表達式(正則表達式):

    • ^...$錨正則表達式,以確保它的整個輸入相匹配( ^匹配輸入的開始,並且$結束)。
    • .{4}匹配第一個4個字符(.),無論它們是什麼。
    • (.+)匹配任何字符非空序列,並且由於被封入(...),捕獲在捕獲組,這反映在自動$Matches變量的序列,如$Matches.1訪問的(由於是第一個捕獲組) 。
    • .匹配文件擴展名之前的字符。
    • \.匹配文字.,由於以\轉義 - 即擴展的開始。
    • (.+)是捕獲文件擴展名(不含前面的.文字)的第2個捕獲組,可以被訪問爲$Matches.2
  • Copy-Item -Destination { $Matches.1 + '.' + $Matches.2 }然後根據從輸入文件名提取的捕獲組值來重命名每個輸入文件。

    • 通常,直接輸送至一個小命令,如果可行的話,始終是最好管道傳遞到小命令Foreach-Object(其內置別名是foreach),出於性能原因。
      在上面的Copy-Item命令中,目標路徑是通過腳本塊參數指定的,對每個輸入路徑使用綁定到輸入文件的$_進行評估。

    • 注:上述假定副本應放置在當前目錄,因爲腳本塊輸出一個單純文件名,而不是一個路徑

      • 要明確控制目標路徑,請在-Destination腳本塊中使用Join-Path
        例如,要確保副本始終放置在與輸入文件相同的文件夾中 - 與當前目錄無關。是 - 使用:
        Join-Path $_.PSParentPath ($Matches.1 + '.' + $Matches.2)

至於你已經試過

  • "..."(雙引號字符串),您必須使用$(...)子表達式運算符,以便嵌入應該用其值替換的表達式。

  • 無論如何,.replace ("AAB", "")(a)由於空格字符而在語法上斷開。 (之前(你迷惑[string]類型的.Replace()方法使用PowerShell的-replace操作?),(B)硬編碼的前綴去掉,(c)是有限的,以字符,和(d)沒有按」在這段時間之前刪除角色。

  • 目的地位置的警告也適用:如果你的表達式的工作,那就只能評估到,這將把生成的文件中當前目錄,而不是同一個目錄中輸入文件(儘管如此,如果您從當前目錄運行命令,或者如果這是您的實際意圖,這不會成爲問題)。

+0

謝謝!你的答案奏效。 PowerShell的正則表達式不符合POSIX標準。你能推薦PowerShell的學習資源嗎? – oavaldezi

+0

PowerShell基於.NET Framework,所以它的正則表達式比POSIX的要求強大得多(這通常是件好事)。至於學習:或許[this](https://www.simple-talk。com/sysadmin/powershell/powershell-one-liners-variables,-parameters,-properties,-and-objects /)是有幫助的,但我也建議熟悉PowerShell自己的幫助系統 - 參見['Get-Help Get-Help '](https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/get-help)。 – mklement0

+0

與一些簡單的SubString/Concatenation操作相比,該正則表達式仍然顯得很醜陋。 – abelenky

0

不是PowerShell中,但批處理文件:

一對夫婦的試驗和錯誤小時後,當我收到這是接近

(因爲有人想成爲超迂腐有關評論)

@echo off 
setlocal enabledelayedexpansion 
for %%a in (*.jpg) do ( 
    ::Save the Extension 
    set EXT=%%~xa 

    ::Get the Source Filename (no extension) 
    set SRC_FILE=%%~na 

    ::Chop the first 4 chars 
    set DST_FILE=!SRC_FILE:~4! 

    ::Chop the last 1 char. 
    set DST_FILE=!DST_FILE:~,-1! 

    :: Copy the file 
    copy !SRC_FILE!!EXT! !DST_FILE!!EXT!) 
+0

警告(可能會也可能不是問題):如果碰巧存在基本名稱中少於5個或更少字符的'* .jpg'文件,則會導致問題。 嘗試使用'??????? *。jpg'來限制與* .jpg文件的匹配,例如7+個字符。在基本名稱中,但這不是一個解決方案 - 令人驚訝的是,該模式還將'* .jpg'文件與_fewer_chars匹配。在基地名稱。 – mklement0

+0

只是對不熟悉批處理文件語法的讀者的提示:在'(...)'塊中使用'::'註釋通常只能在有限的場景和[規則](http:// stackoverflow。 com/a/42148114/45375)很難記住。相比之下,使用'REM'是安全的。 – mklement0

-1

在PowerShell中:
(沒有討厭的正則表達式。我們討厭正則表達式!我們所做的)上表達

Get-ChildItem *.jpg | Copy-Item -Destination {($_.BaseName.Substring(4) -replace ".$")+$_.Extension} -WhatIf 

!詳細說明:

$_.BaseName.Substring(4) :: Chop the first 4 letters of the filename. 
-replace ".$"    :: Chop the last letter. 
+$_.Extension    :: Append the Extension 
+0

警告(可能會也可能不是問題):'.Substring()'調用將打破基本名稱少於4個字符的文件名。 – mklement0

+0

這是海報問題。他的問題意味着文件名足夠長。 – abelenky

+1

我看到你已經與魔鬼達成了一個小協議 - 順勢療法劑量的正則表達式('。$')。 我很高興陪練導致更好的答案。請問你的驢子。 – mklement0

-1

試試這個:

Get-ChildItem "C:\temp\Test" -file -filter "*.jpg" | where BaseName -match '.{4,}' | 
    %{ Copy-Item $_.FullName (Join-Path $_.Directory ("{0}{1}" -f $_.BaseName.Substring(4, $_.BaseName.Length - 5), $_.Extension)) }