Değişken olan birçok çok uzun dosya adını yeniden adlandırmanız gerekiyor


2

Şirketim her gün işleme koymamız gereken dosyaların bir listesini alır ve dosya adlarının sistemimizin üstesinden gelmesi neredeyse imkansızdır. Bu dosyaları yeniden adlandırmanın bir yolu var mı? Herhangi bir komut dosyası türünde yeniyim, bu nedenle nereden başlayacağımı bilmiyorum. Windows sistemi kullanıyorum. Toplu Yeniden Adlandırma Yardımcı Programını kullanmaya çalıştım, ancak AB_C_D_'nin nasıl kaldırılacağını çözemiyorum ve bazen çözemediğim bilinmeyen bir nedenden dolayı hatalar yapıyor. PowerShell kullanarak bu dosyaları yeniden adlandırmanın bir yolu var mı?

Bu, dosya adlarının şimdi nasıl göründüğü:

Sample1_Sample2_1_05-11-2015_0_Sample3-AB_C_D_045_4_Sample4_123456.pdf

Yapmak istediğim şey bu:

  • Kaldır Sample1(her zaman aynı olacak)
  • Bırakın Sample2dosya ismi ile başlasın Sample2(her zaman aynı olacak)
  • Kaldır _1
  • Tarihi terk edin (ki bu gelecekteki bir tarihtir ve değişecektir)
  • 0_Sample3(Her zaman aynı olan) çıkarın
    -Sayfa numarasını bırakın (her dosyada farklı olacaktır 045) ve tarihten sonra yerleştirin.
    -Remove _4_Sample4_ -Lve
    123456 (bu bir tanımlayıcı numaradır ve her dosya için farklı olacaktır).

Asıl sorun, AB_C_D_'yi kaldırmak istiyorum ve bu harfler değişecek. Daha fazla veya daha az olabilir (örneğin A_C_D_) ve bu parçanın nasıl çıkarılacağını bilmiyorum.


Yani bitmiş dosya adı olacak Sample2_05-11-2015_045_123456.pdf

Birisi bana bu konuda yardımcı olabilir ya da bunun nasıl yapılacağı konusunda bana doğru yönde işaret ederse, çok takdir edilecektir!

Şimdiden teşekkürler, HH-GeekyGal


Bu , BRU veya PowerShell ile istediğinizi yapmanıza yardımcı olacaktır.
Karan

Yanıtlar:


1

Bu Powershell betiği, dosyaları istediğiniz şekilde yeniden adlandıracak. Farklı kaydedin RenameFiles.ps1ve PowerShell konsolundan çalıştırın.

Script aşağıdaki argümanları kabul eder:

  • Yol : Gerekli, diskinizde, dosyalarınızın depolandığı mevcut klasör. Birden çok yol sağlayabilirsiniz.
  • Tekrarlama : İsteğe bağlı anahtar, tekrarı kontrol eder. Belirtilirse, komut dosyası tüm alt klasörlerdeki dosyaları yeniden adlandırır.
  • WhatIf : İsteğe bağlı anahtar, belirtilirse, komut dosyası yalnızca yeni ve eski dosya adlarını bildirir. Yeniden adlandırma yapılmayacak.

Örnekler (PowerShell konsolundan çalıştırın):

  • Klasördeki tüm dosyaları yeniden adlandırın c:\path\to\files:

    .\RenameFiles.ps1 -Path 'c:\path\to\files'
  • pdfKlasördeki tüm dosyaları yeniden adlandırın c:\path\to\files:

    .\RenameFiles.ps1 -Path 'c:\path\to\files\*.pdf'
  • pdfKlasördeki tüm dosyaları yeniden adlandır c:\path\to\files, tekrarla

    .\RenameFiles.ps1 -Path 'c:\path\to\files\*.pdf' -Recurse
  • Dosyaları birden fazla klasörde tarayın, tekrarlayın, yalnızca bildirin (yeniden adlandırma yok):

    .\RenameFiles.ps1 -Path 'c:\path\A\*.pdf', 'c:\path\B\*.psd' -Recurse -WhatIf

RenameFiles.ps1 betiğin kendisi:

# Arguments accepted by script
Param
(
    # One or multiple paths, as array of strings
    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [string[]]$Path,

    # Recurse switch
    [switch]$Recurse,

    # Whatif switch
    [switch]$WhatIf
)

# This function transforms long file name (w\o extension) to short via regex
function Split-FileName
{
    [CmdletBinding()]
    Param
    (
        # Original file name
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$FileName
    )

    Begin
    {
        # You can change this block to adapt new rules for file renaming,
        # without modifying other parts of script.

        # Regex to match, capture groups are used to build new file name
        $Regex = '(Sample2).*(\d{2}-\d{2}-\d{4}).*(?<=[a-z]_)(\d+)(?=_\d+).*(?<=_)(\d+)$'

        # Scriptblock that builds new file name. $Matches is hashtable, but we need array for the format (-f) operator.
        # So this code: @(0..$Matches.Count | ForEach-Object {$Matches[$_]})} transforms it to the array.

        # Basically, we creating a new array of integers from 0 to count of $Matches keys, e.g. @(0,1,2,3,4,5)
        # and passing it down the pipeline. Then, in the foreach loop we output values of $Matches keys which name
        # match the current pipeline object, e.g. $Matches['1'], $Matches['2'], etc.
        # $Matches['0'] holds whole matched string, other keys hold capture groups.

        # This would also work:
        # $NewFileName = {'{0}_{1}_{2}_{3}{4}' -f $Matches['1'], $Matches['2'], $Matches['3'], $Matches['4'], $Matches['5']

        $NewFileName = {'{1}_{2}_{3}_{4}{5}' -f @(0..$Matches.Count | ForEach-Object {$Matches[$_]})}

    }

    Process
    {
        # If original file name matches regex
        if($FileName -match $Regex)
        {
            # Call scriptblock to generate new file name
            . $NewFileName
        }
    }
}

# For each path, get all file objects
Get-ChildItem -Path $Path -Recurse:$Recurse |
    # That are not directory
    Where-Object {!$_.PsIsContainer} |
        # For each file
        ForEach-Object {
            # Try to create new file name
            $NewBaseName = $_.BaseName | Split-FileName

            if($NewBaseName)
            {
                # If file name matched regex and we've got a new file name...

                # Build full path for the file with new name
                $NewFullName = Join-Path -Path $_.DirectoryName -ChildPath ($NewBaseName + $_.Extension)

                if(Test-Path -Path $NewFullName -PathType Leaf)
                {
                    # If such file already exists, show error message
                    Write-Host "File already exist: $NewFullName"
                }
                else
                {
                    # If not, rename it or just show report, depending on WhatIf switch
                    Rename-Item -Path $_.FullName -NewName $NewFullName -WhatIf:$WhatIf -Force
                }
            }
    }

Bu komut dosyasında kullanılan Regex: https://regex101.com/r/hT2uN9/2 (Not, varsayılan olarak PowerShell'in düzenli ifadesinin büyük / küçük harf duyarlı olmadığını unutmayın). Regex açıklamasının kopyası burada:

Regex :

(Sample2).*(\d{2}-\d{2}-\d{4}).*(?<=[a-z]_)(\d+)(?=_\d+).*(?<=_)(\d+)$

Sample2 dizesi:

1st Capturing group (Sample2)

Sample2 matches the characters Sample2 literally (case insensitive)

Herhangi bir karakter (yakalanmadı ve $Matchesdeğişkende bulunmuyor ):

.* matches any character (except newline)
Quantifier: * Between zero and unlimited times, as many times as possible,
giving back as needed [greedy]

Tarih :

2nd Capturing group (\d{2}-\d{2}-\d{4})

\d{2} match a digit [0-9]
Quantifier: {2} Exactly 2 times
- matches the character - literally

\d{2} match a digit [0-9]
Quantifier: {2} Exactly 2 times
- matches the character - literally

\d{4} match a digit [0-9]
Quantifier: {4} Exactly 4 times

Herhangi bir karakter (yakalanmadı ve $Matchesdeğişkende bulunmuyor ):

.* matches any character (except newline)
Quantifier: * Between zero and unlimited times, as many times as possible,
giving back as needed [greedy]

Sayfa sayısı :

(?<=[a-z]_) Positive Lookbehind - Assert that the regex below can be matched

[a-z] match a single character present in the list below
a-z a single character in the range between a and z (case insensitive)
_ matches the character _ literally

3rd Capturing group (\d+)

\d+ match a digit [0-9]
Quantifier: + Between one and unlimited times, as many times as possible,
giving back as needed [greedy]

(?=_\d+) Positive Lookahead - Assert that the regex below can be matched
_ matches the character _ literally

\d+ match a digit [0-9]
Quantifier: + Between one and unlimited times, as many times as possible,
giving back as needed [greedy]

Herhangi bir karakter (yakalanmadı ve $Matchesdeğişkende bulunmuyor ):

.* matches any character (except newline)
Quantifier: * Between zero and unlimited times, as many times as possible,
giving back as needed [greedy]

Kimlik numarası :

(?<=_) Positive Lookbehind - Assert that the regex below can be matched
_ matches the character _ literally

4th Capturing group (\d+)

\d+ match a digit [0-9]
Quantifier: + Between one and unlimited times, as many times as possible,
giving back as needed [greedy]

Soruma cevap veren herkese teşekkürler. Herhangi bir komut dosyası türünde yeniyim olduğundan, bunu birkaç gün boyunca sindirmek zorunda kalacağım. Bu, bu forumdaki ilk sorum ve anlaşılabilir bir şekilde söylemek zordu.
Heidi,

0

Karan bağlantılı olduğu gibi, düzenli ifadeler bunu yapmanın yoludur. Linux'tayım, yani powershell'in uygun yapılara sahip olup olmadığından emin değilim, ancak değilse, sourceforge'dan windows için sed'i indirin. Etrafta garip bir durum var.

Sed-fu'm korkunç bir şey ama bu orijinal dizgiyi yenisiyle değiştirecek:

sed -r 's/Sample1_(Sample2_)[0-9]*_(..-..-....)_.*-[A-Z_]*(_[0-9][0-9]*_)._Sample4_(.)/\1\2\3\4/'

Aynı şeyi başarmanın daha basit yolları olduğuna eminim.

Bash okuyabiliyorsanız, aşağıda onunla yeniden adlandırmak için bir örnek:

for i in $(ls);do mv $i $(echo $i|sed -r 's/Sample1_(Sample2_)[0-9]*_(..-..-....)_.*-[A-Z_]*(_[0-9][0-9]*_)._Sample4_(.*)/\1\2\3\4/');done

Hiç şüphe yok ki powershell'de benzer komut dosyası yazmak yeterince basit ancak okuyucuya alıştırma olarak kaldı: P

DÜZENLEME: yazım hatası

EDIT2: Yazdığım şeye baktım ve anlamak zor olabilir, bu yüzden ne yapmaya çalıştığımı deneyeceğim ve göstereceğim:

Genel olarak regex satırı okur ve parantez içinde tutmak istediğimiz kısımları çevreler. Bunlara kalıp denir. Satır okunduktan sonra, seçilen desenler dışındaki her şeyi atın.

sed -r   //-r switch is here only to allow the use of parens without escaping them. It's confusing enough without backslashes.
's/      //s is the command, stands for subtitute. syntax s/[search pattern]/[replace pattern]/. string matching SP is replaced with RP.
         //Here I use the command to match the whole line and save the parts I want.

Sample1_(Sample2_)  //set "Sample2_" as first pattern
[0-9]*_(..-..-....) //read onwards and skip zero or more numerals ([0-9]*) between two underscores. Read xx-xx-xxxx as second pattern where x is any character
_.*-[A-Z_]*(_[0-9][0-9]*_) //after underscore, skip any number of characters (.*) until run across dash. after that, skip any number of capital letters and underscores until you run into underscore followed by more than one numeral and underscore (_[0-9][0-9]*_). Save that as pat 3
._Sample4_(.*) //grab everything after Sample4_ as pat 4
/\1\2\3\4/'   //First slash ends the search pattern for the s command and begin the . After that, \1, \2, \3 and \4 insert patterns we saved in search part discarding the rest. final slash ends the s command.

Regex okumak zor olsa bile yazmak kolaydır. Bu da hata yapmak zor ve hata ayıklamanın zor olduğu, ancak hepsine sahip olamayacağınız anlamına gelir.

İşte basic / python / pseudocode-ish scribble içindeki kabuk betiğinin özeti.

for OLDNAME in DIRECTORY
     let NEWNAME = output of sed command with OLDNAME piped as input.
     rename OLDNAME NEWNAME
next
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.