简体   繁体   English

PowerShell:脚本无法打开存档中的本机文件,但能够打开新创建的文件

[英]PowerShell : Script unable to open native file in archive, but able for newly created file

Hi guys !嗨,大家好 !

I've created a powershell script that goes through some archive files to do some simple text editing in one of the files.我创建了一个 powershell 脚本,它通过一些存档文件在其中一个文件中进行一些简单的文本编辑。

My problem is rather simple, when I want to open and edit xxx.ini, it fails.我的问题很简单,当我想打开和编辑 xxx.ini 时,它失败了。 Yet, when I add a test.ini (or delete xxx.ini, and add it back) , and try to run the exact same script on this new file, it works.然而,当我添加一个 test.ini (或删除 xxx.ini,然后将其重新添加)并尝试在这个新文件上运行完全相同的脚本时,它就可以工作了。

I tried looking for the IsReadOnly property, but it returns false.我尝试查找IsReadOnly属性,但它返回 false。

Can anyone help with this?有人能帮忙吗?

        if ($f -notmatch "bulkPrefEditor"){
            # The zip file to be updated
            $file = $f

            # Is the file read only ? Let's try to force that to false
            $isReadOnly = Get-ItemProperty -Path $file | Select-Object IsReadOnly
            Write-Log "Is $file Read-Only : $isReadOnly" "INFO" $logfile
            Set-ItemProperty -Path $file -Name IsReadOnly -Value $false


            Write-Log "Editing following file : $f" "INFO" $logfile

            # Load ZipFile (Compression.FileSystem) if necessary
            try { $null = [IO.Compression.ZipFile] }
            catch { [System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') }

            # Open zip file with update mode (Update, Read, Create -- are the options)
            try { 
                $fileZip = [System.IO.Compression.ZipFile]::Open( $file, 'Update' )
            }
            catch {
                Write-Log "Another process has locked the '$file' file." "ERROR" $logfile
                Write-Log "Another process has locked the '$file' file."
                continue
            }

            # Finding the specific file within the zip file
            try{
                $fileZip.Entries | Where-Object { $_.FullName -match "$innerpath" }
            } catch {
                Write-Log "Could not find $filepath\$innerpath in $f , have you used anti-slash instead of slash ?" "WARN" $logfile
                Write-Log "Could not find $filepath\$innerpath in $f , have you used anti-slash instead of slash "
                continue
            }
            pause
            # If needed, read the contents of specific file to $text and release the file so to use streamwriter later
            try{
                $desiredFile = [System.IO.StreamReader]($fileZip.Entries | Where-Object { $_.FullName -match "$innerpath" }).Open()
                $text = $desiredFile.ReadToEnd()
                $desiredFile.Close()
                $desiredFile.Dispose()
            }
            catch {
                Write-Log "Could not read $f/$innerpath in $f " "WARN" $logfile
                Write-Log "Could not read $f/$innerpath in $f "
                continue
            }

Result:结果: 在此处输入图像描述

I've added some simple logs to my script:我在脚本中添加了一些简单的日志:

2019/11/21 18:40:12 INFO #################### STARTING SCRIPT... #################### 2019/11/21 18:40:12 INFO This Script will operate on files contained in C:\Users\pfournet\Documents\Factory\BulkPrefEditor 2019/11/21 18:40:12 INFO This script can see (4) files. 2019/11/21 18:40:12 INFO ################### 启动脚本... ############## ###### 2019/11/21 18:40:12 INFO 此脚本将对 C:\Users\pfournet\Documents\Factory\BulkPrefEditor 中包含的文件进行操作 2019/11/21 18:40:12 INFO 此脚本可以看到(4)个文件。 2019/11/21 18:40:12 INFO List of available files (single line): 20191119-123448387 - Copie (2).zip 20191119-123448387 - Copie.zip 20191119-123448387.zip BulkPrefEditor.ps1 BulkPrefEditor_Zip.ps1 2019/11/21 18:40:12 INFO This file has been found: 20191119-123448387 - Copie (2).zip 2019/11/21 18:40:12 INFO This file has been found: 20191119-123448387 - Copie.zip 2019/11/21 18:40:12 INFO This file has been found: 20191119-123448387.zip 2019/11/21 18:40:12 INFO This Script will replace this line: use-compression = true with use-compression = false 2019/11/21 18:40:12 INFO #################### COMMENCING OPERATIONS #################### 2019/11/21 18:40:12 INFO Is 20191119-123448387 - Copie (2).zip Read-Only: @{IsReadOnly=False} 2019/11/21 18:40:12 INFO Editing following file: 2 2019/11/21 18:40:12 INFO List of available files (single line): 20191119-123448387 - Copie (2).zip 20191119-123448387 - Copie.zip 20191119-123448387.zip BulkPrefEditor.ps1 BulkPrefEditor_Zip.ps1 2019/ 11/21 18:40:12信息已找到此文件:20191119-123448387 -Copie(2).zip 2019/2021/21/21/21/21/21 18:40:12 INFO THIN222221B. 2011BIE:20191119-119-123488888888888888884888484848484848484848484848484848484848484848484848484848488848 lar /11/21 18:40:12 INFO 此文件已找到:20191119-123448387.zip 2019/11/21 18:40:12 INFO 此脚本将替换此行:use-compression = true 与 use-compression = false 2019/11/21 18:40:12 INFO ################### 开始运营################ ### 2019/11/21 18:40:12 INFO 是 20191119-123448387 - 副本 (2).zip 只读:@{IsReadOnly=False} 2019/11/21 18:40:12 INFO 编辑以下文件: 2 0191119-123448387 - Copie (2).zip 2019/11/21 18:40:13 WARN Could not read 20191119-123448387 - Copie (2).zip/ul-flow/plugins/interactivedata.ini in 20191119-123448387 - Copie (2).zip 2019/11/21 18:40:13 INFO Is 20191119-123448387 - Copie.zip Read-Only: @{IsReadOnly=False} 2019/11/21 18:40:13 INFO Editing following file: 20191119-123448387 - Copie.zip 2019/11/21 18:40:13 WARN Could not read 20191119-123448387 - Copie.zip/ul-flow/plugins/interactivedata.ini in 20191119-123448387 - Copie.zip 2019/11/21 18:40:13 INFO Is 20191119-123448387.zip Read-Only: @{IsReadOnly=False} 2019/11/21 18:40:13 INFO Editing following file: 20191119-123448387.zip 2019/11/21 18:40:14 WARN Could not read 20191119-123448387.zip/ul-flow/plugins/interactivedata.ini in 20191119-123448387.ZADCDBD79A8D84 0191119-123448387 - 复制 (2).zip 2019/11/21 18:40:13 警告无法读取 20191119-123448387 - 复制 (2).zip/ul-flow/plugins/19-191.ini in 24481721 (2)。 -123448387 - Copie.zip 2019/11/21 18:40:13 WARN Could not read 20191119-123448387 - Copie.zip/ul-flow/plugins/interactivedata.ini in 20191119-123448387 - Copie.zip 2019/11/21 18:40:13 INFO Is 20191119-123448387.zip Read-Only: @{IsReadOnly=False} 2019/11/21 18:40:13 INFO Editing following file: 20191119-123448387.zip 2019/11/21 18:40 :14 WARN 无法读取 20191119-123448387.ZADCDBD79A8D84 中的 20191119-123448387.zip/ul-flow/plugins/interactivedata.ini 175C229B192AADC02F2Z 2019/11/21 18:40:14 INFO #################### ENDING SCRIPT... #################### 175C229B192AADC02F2Z 2019/11/21 18:40:14 信息 #################### 结束脚本... ############# #######

As Rich Moss mentioned in their comment, your script as written is not disposing of the objects and you are keeping locks out on the zip file.正如 Rich Moss 在他们的评论中提到的那样,您编写的脚本不会处理对象,并且您正在锁定 zip 文件。 My initial solution to this was to extract all the files from the archive, modify the file that you need, then rezip.我最初的解决方案是从存档中提取所有文件,修改您需要的文件,然后重新压缩。

Add-Type -AssemblyName System.IO.Compression.FileSystem

$file = Get-ChildItem C:\temp\test3.zip

$tempOutputFolder = "c:\temp\$(New-Guid)"

[System.IO.Compression.ZipFile]::ExtractToDirectory($file.FullName, $tempOutputFolder)

$zippedFilesToEdit = Get-ChildItem $tempOutputFolder -Recurse -File | Where-Object { $_.Name -eq "new 1" }

foreach ($zippedFile in $zippedFilesToEdit){
    #Modifiy your file, and save it
    Get-Content $zippedFile | for-each {$_ -replace "Pattern","ReplaceWith"} | Out-File $zippedFile.FullName
}

Remove-Item $file.FullName
[System.IO.Compression.ZipFile]::CreateFromDirectory($tempOutputFolder,$file.FullName)

This is not as flexible as modifying the file object directly from the archive.这不如直接从存档中修改文件 object 灵活。 If you have a big archive/files this will take too long, so I sat down to do it via a stream.如果你有一个很大的存档/文件,这将花费太长时间,所以我坐下来通过 stream 来完成它。 This script works, but I am sure there could be improvements.该脚本有效,但我相信可能会有改进。 (I think we could actually just edit the stream directly) Also, the error handling could be better. (我认为我们实际上可以直接编辑 stream)此外,错误处理可能会更好。 We want to make sure we close/dispose of all the objects on completion of failure, so we need a try/catch/finally block around the whole thing, which requires us to check for individual errors, repackage them and throw (`Write-Error -ErrorAction Stop) them again.我们希望确保在失败完成时关闭/处理所有对象,因此我们需要一个 try/catch/finally 块围绕整个事物,这需要我们检查单个错误,重新打包并抛出(`Write- Error -ErrorAction Stop) 再次。 This can make debugging harder, because you will not see the actual line number of the error.这会使调试更加困难,因为您不会看到错误的实际行号。 You have to make sure when you catch the errors that you are properly handling both expected and unexpected errors properly.您必须确保在捕获错误时正确处理预期和意外错误。

This runs for me on Win 10/PS 5.1.这适用于我在 Win 10/PS 5.1 上运行。

EDIT I added code at the beginning and end which will test this out by creating a zip archive with 10 generated files, reporting the size, then performing the edit operation and unzipping to check the size of the modified files.编辑我在开头和结尾添加了代码,它将通过创建一个包含 10 个生成文件的 zip 存档来测试这一点,报告大小,然后执行编辑操作并解压缩以检查修改文件的大小。 This code will remove files from the $Test* locations.此代码将从 $Test* 位置删除文件。 Read the script before you run it so you know what it will do before you hit F5在运行之前阅读脚本,以便在按下 F5 之前知道它会做什么

$TestPath = "C:\temp\zipTest"
$testZipPath = "C:\temp\ZipTest.zip"
$filePathToFind = "test 1.txt"

If (-not (Test-Path $TestPath)){New-Item -Path $TestPath -ItemType Directory}
foreach( $i in 1..10){
   "Some Text/file contents" | Set-Content -Path "$TestPath\test $i.txt" 
}
Remove-Item -Path $testZipPath -Force -ErrorAction SilentlyContinue | Out-Null
[System.IO.Compression.ZipFile]::CreateFromDirectory($TestPath,$testZipPath)

Write-Host "File sizes before any operation: "
Get-ChildItem $testZipPath | foreach { Write-Host "filename: $($_.Name), length: $($_.Length)"}
Write-Host "test zip file size: $(Get-ChildItem $testZipPath | select -ExpandProperty Length)"

$file = $testZipPath
Write-Host ""
Write-Host "Running the opertion to modify the file in the archive"
try{

    # Check if zip files exists, and can open the file, then open if possible
    If(-not (Test-Path $file)){Write-Error "Zip File does not exist" -ErrorAction Stop}
    try { $fileZip = [System.IO.Compression.ZipFile]::Open( $file, 'Update' ) }
    catch { Write-Error "Another process has locked the '$file' file." -ErrorAction Stop }

    # Finding the specific file within the zip file
    try{ $fileZip.Entries | Where-Object { $_.FullName -eq $filePathToFind } | Out-Null} 
    catch { Write-Error "Could not find $filePathToFind in $file." -ErrorAction Stop }

    # If needed, read the contents of specific file to $text and release the file so to use streamwriter later
        #Get the entry in the archive that you want to modify
        $zippedEntry = $fileZip.Entries | Where-Object { $_.FullName -eq $filePathToFind }
        if($zippedEntry -eq $null){Write-Error "Could not find entry in zip file" -ErrorAction Stop}
        #Open the file as a stream
        $desiredFile = [System.IO.StreamReader]($zippedEntry).Open() 
        #Read the entire contents to a string variable (Maybe you could manipulate the stream but not sure how to do that)
        $text = $desiredFile.ReadToEnd()

        #Cleanup
        $desiredFile.Close() | Out-Null
        $desiredFile.Dispose()| Out-Null

        #Modify the contents as needed. In this case I am just setting the contents to a bunch of numbers
        [string]$newText = ((1..1000) | % {"$_`n"})

        #Delete the entry in the archive
        $zippedEntry.Delete()
        #Create a new (empty) entry at the path inside the archive
        $newEntry = $fileZip.CreateEntry($filePathToFind)

        #Open the new entry as a stream and write the new text to it
        $stream = [System.IO.StreamWriter] $newEntry.Open()
        $stream.Write($newText)
        $stream.Close()
}
catch {
    throw
}
finally{
    # You want to dispose of everything in a finally block so that the objects get removed even if an error is thrown 
    $desiredFile.Close() | Out-Null
    $desiredFile.Dispose()| Out-Null
    $stream.Close() | Out-Null
    $fileZip.Dispose()| Out-Null
}
Write-Host "Zipped file update complete"
Write-Host ""

# Check zip size and individual file sizes after operation (Just to test. Will delete files on your computer!!!!)
Write-Host "test zip file size after replacement: $(Get-ChildItem $testZipPath | select -ExpandProperty Length)"
$FollowUpTestPath = "$(Split-Path $testZipPath -Parent)\FollowUpTest"
Remove-Item $FollowUpTestPath -Recurse -Force -ErrorAction SilentlyContinue
If(-not (Test-Path $FollowUpTestPath)){New-Item -Path $FollowUpTestPath -ItemType Directory | Out-Null}
[System.IO.Compression.ZipFile]::ExtractToDirectory($testZipPath, $FollowUpTestPath)
Write-Host "File sizes after operation: "
Get-ChildItem $FollowUpTestPath | foreach { Write-Host "filename: $($_.Name), length: $($_.Length)"}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM