如何在Windows上的1个语句中压缩文件夹内容? [英] How To Compress Folder-Contents in 1 Statement on Windows?

查看:395
本文介绍了如何在Windows上的1个语句中压缩文件夹内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Windows shell CopyHere 命令来压缩包含子文件夹和项目的文件夹:



https://msdn.microsoft。 com / en-us / library / windows / desktop / bb787866(v = vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms723207(v = vs.85) .aspx



更新:注意,更喜欢本地解决方案 - 这是一个分布式Excel VBA工具, -party文件不是理想的。并且,需要同步压缩。



我可以轻松地将文件夹添加到zip文件中:

  oShell.Namespace(sZipPath).CopyHereC:\ My Folder


$ b b

所以我们知道可以在1个语句中处理文件夹中的多个对象。



问题是,上面的命令将contains文件夹放在zip的根目录,它的内容。但是,我不想要的包含文件夹 - 只是其内容。



文档提到通配符(选项128),但是当我使用通配符时,我得到一个错误:

  oShell.Namespace(sZipPath).CopyHereC:\ My Folder \ *




您指定的文件名无效或太长。


也许有一种方法可以使用上面的第一个命令,然后将zip中的项目移动到zip的根目录? / p>

可以循环遍历源文件夹中的每个项目,一次添加一个到zip。但是,因为CopyHere是异步的,如果先前的CopyHere未完成,则每个后续CopyHere都会失败。对于此问题,所有修复都无效:




  • 比较source-folder和destination-zip中的项目数,该zip包含一个文件夹,计数为只有1个项目(它包含的项目不计算在内。 http://stackoverflow.com/a / 16603850/209942


  • 在每个项目之间等待一段时间,但计时器是不可接受的:它是任意的,我不能提前猜出大小


  • 检查zip是否被锁定以供访问失败如果我阻塞我的循环,直到文件不是锁定,我仍然收到文件访问错误。 http://stackoverflow.com/a/6666663/209942







 功能FileIsOpen(sPathname As String)As Boolean'true如果文件打开
Dim lFileNum As Long
lFileNum = FreeFile
Dim lErr As Long
在错误恢复下
打开sPathname用于二进制访问读写锁定读写#lFileNum
lErr = Err
关闭#lFileNum
错误时转到0
FileIsOpen =(lErr<> 0)
结束函数

更新: VBA可以调用shell命令同步(而不是在VBA中创建一个 shell32.shell 对象),所以如果CopyHere在命令行或PowerShell上工作,那可能是解决方案。正在调查...

解决方案

解决方案



Windows在PowerShell提示中包含另一个本机压缩实用程序: CreateFromDirectory



https://msdn.microsoft.com/en-us/library/system.io.compression.zipfile.createfromdirectory(v = vs.110).aspx



https://blogs.technet.microsoft.com/heyscriptingguy/2015/03/09/use-powershell-to-create-zip-archive-of-folder/ a>



这需要.Net 4.0或更高版本:

  ;添加类型-AssemblyName System.IO.Compression 
> $ src =C:\Users\v1453957\documents\Experiment\rezip\aFolder
> $ zip =C:\Users\v1453957\Documents\Experiment\rezip\my.zip
> [io.compression.zipfile] :: CreateFromDirectory($ src,$ zip)

必须提供完整的路径名 - 活动目录不是隐含在我的机器上。



上述压缩是PowerShell提示下的同步,作为OP请求。






下一步是从VBA同步执行。解决方案是 .Run 方法).aspxrel =nofollow> Windows脚本宿主对象模型。在VBA中,设置一个引用,并执行以下操作,设置 .Run 命令的第三个参数, bWaitOnReturn True



函数SynchronousShell(sCmd As String)As Long
Dim oWSH As New IWshRuntimeLibrary.WshShell
ShellSynch = oWSH.Run(sCmd,3,True)
设置oWSH =无
结束函数



现在调用 SynchronousShell ,并传递整个压缩脚本。



我相信这个过程的唯一方法是如果 CreateFromDirectory 在同一个会话中执行 Add-Type



因此,我们必须将整个内容作为1个字符串传递。也就是说,将所有4个命令加载到单个 sCmd 变量中,以使 Add-Type code> CreateFromDirectory 。在PowerShell语法中,您可以使用;


$ b

https://thomas.vanhoutte.be/miniblog/execute-multiple-powershell-commands-on-one- line /



此外,您还需要使用单引号而不是双引号,否则字符串周围的双引号将被删除,链接的命令传递到powershell.exe



http://stackoverflow.com / a / 39801732/209942

  sCmd =ps4 Add-Type -AssemblyName System.IO.Compression; $ src ='C:\Users\v1453957\documents\Experiment\rezip\aFolder'; $ zip ='C:\Users\v1453957\Documents\Experiment\rezip\my。 zip'; [io.compression.zipfile] :: CreateFromDirectory($ src,$ zip)

已解决。以上构成完整的解决方案。






额外资讯:下列其他评论适用于特殊情况:



多版本.Net环境



如果.NET< 4.0是您的操作系统上的活动环境,则 System.IO.Compression 不存在 - Add-Type 命令将失败。但是如果你的机器有可用的.NET 4程序集,你仍然可以这样做:




  • 创建一个运行PowerShell的批处理文件with .Net 4.请参见 http://stackoverflow.com/a/31279372


  • 在上面的 Add-Type 命令中,使用.Net 4压缩程序集的确切路径。在我的Win Server 2008上:




Add-Type -PathC:\Windows \Microsoft.NET\assembly\GAC_MSIL\System.IO.Compression.FileSystem\v4.0_4.0.0.0__b77a5c561934e089\System.IO.Compression.FileSystem.dll



可移植性



原来,在我的机器上,我可以将压缩dll复制到任何文件夹,并调用该副本,它的工作原理:



添加类型-PathC:\MyFunnyFolder\System.IO .Compression.FileSystem.dll



我不知道需要什么来确保这个工作 - 它可能需要完整的.Net 4.0或2.0文件位于其预期目录中。我假设dll调用其他.Net程序集。也许我们只是很幸运这个:)



字符限制



在我们的路径和文件名的深度,字符计数可能是一个问题。 PowerShell可能有260个字符的限制(不确定)。



https:// support。 microsoft.com/en-us/kb/830473



https://social.technet.microsoft.com/Forums/windowsserver/en -US / f895d766-5ffb-483f-97bc-19ac446da9f8 / powershell-command-size-limit?forum = winserverpowershell



由于 .Run 通过Windows shell,你也必须担心字符限制,但在8k +,它有点宽敞:
https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553
http://stackoverflow.com/a/3205048/209942



以下网站提供了一个24k +字符的方法,但我还没有研究过:
http://itproctology.blogspot.com/2013/06/handling-freakishly-long-strings-from.html



至少,因为我们可以把dll放在任何我们喜欢的地方,我们可以把它放在C:root附近的文件夹中 - 保持我们的字符计数。



更新: 此信息显示我们如何将整个东西放在一个脚本文件中,并用ps4.cmd调用它。这可能成为我的首选答案:



.\ps4.cmd GC .\zipper.ps1 | IEX



- 取决于回答此处






CopyHere



重新的问题:可以 CopyHere 在命令行执行命令?



CopyHere 可以直接在PowerShell提示符下执行(代码如下)。然而,即使在powershell中它是异步的 - 控制在过程完成之前返回到PowerShell提示符。因此,没有OP的解决方案。操作方法如下:

 > $ shellapp = new-object -com shell.application 
> $ zippath =test.zip
> $ zipobj = $ shellapp.namespace((Get-Location).Path +\ $ zippath)
> $ srcpath =src
> $ srcobj = $ shellapp.namespace((Get-Location).Path +\ $ srcpath)
> $ zipobj.Copyhere($ srcobj.items())


I'm attempting to zip a folder containing subfolders and items, using Windows shell CopyHere command:

https://msdn.microsoft.com/en-us/library/windows/desktop/bb787866(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms723207(v=vs.85).aspx

Update: Note, prefer a native solution-- this is for a distributed Excel VBA tool, so bundling 3rd-party files is not ideal. And, need synchronous compression.

I can easily add a folder and its contents to the zip:

oShell.Namespace(sZipPath).CopyHere "C:\My Folder"

So we know CopyHere can process multiple objects inside a folder in 1 statement.

The problem is, the above command puts the containing-folder at the root of the zip, and it's contents inside of it. But, i don't want the containing folder-- just its contents.

The doc mentions a wildcard (option 128), but when i use a wildcard, i get an error:

oShell.Namespace(sZipPath).CopyHere "C:\My Folder\*"

The file name you specified is not valid or too long.

Perhaps there's a way to use my 1st command above, and then move the items in the zip to the root of the zip?

It would be acceptable to loop through each item in the source folder, adding one at a time to the zip. But, because CopyHere is asynchronous, each subsequent CopyHere fails if the previous CopyHere is not finished. None of the fixes work for this issue:

  • Comparing number of items in source-folder and destination-zip fail, because if the zip contains a folder, that counts as only 1 item (the items it contains are not counted. http://stackoverflow.com/a/16603850/209942

  • Waiting a while between each item works, but a timer is unacceptable: it's arbitrary. I cannot guess in advance the size or compress-time of each object.

  • Checking to see if the zip is locked for access failed for me. If I block my loop until the file is not locked, I still get a file-access error. http://stackoverflow.com/a/6666663/209942


Function FileIsOpen(sPathname As String) As Boolean ' true if file is open
    Dim lFileNum As Long
    lFileNum = FreeFile
    Dim lErr As Long
    On Error Resume Next
    Open sPathname For Binary Access Read Write Lock Read Write As #lFileNum
    lErr = Err
    Close #lFileNum
    On Error GoTo 0
    FileIsOpen = (lErr <> 0)
End Function

Update: VBA can call shell commands synchronously (instead of creating a shell32.shell object in VBA), so if CopyHere works on command-line or PowerShell, that could be the solution. Investigating...

解决方案

Solution:

Windows contains another native compression utility: CreateFromDirectory at a PowerShell prompt.

https://msdn.microsoft.com/en-us/library/system.io.compression.zipfile.createfromdirectory(v=vs.110).aspx

https://blogs.technet.microsoft.com/heyscriptingguy/2015/03/09/use-powershell-to-create-zip-archive-of-folder/

This requires .Net 4.0 or later:

> Add-Type -AssemblyName System.IO.Compression
> $src = "C:\Users\v1453957\documents\Experiment\rezip\aFolder"
> $zip="C:\Users\v1453957\Documents\Experiment\rezip\my.zip"
> [io.compression.zipfile]::CreateFromDirectory($src, $zip)

Note, you may have to provide the complete pathnames-- active directory was not implicit on my machine.

The above compression is synchronous at the PowerShell prompt, as the OP requests.


Next step is executing synchronously from VBA. The solution there is the .Run method in Windows Script Host Object Model. In VBA, set a reference to that, and do the following, setting the 3rd parameter of .Run command, bWaitOnReturn to True:

Function SynchronousShell(sCmd As String)As Long Dim oWSH As New IWshRuntimeLibrary.WshShell ShellSynch = oWSH.Run(sCmd, 3, True) Set oWSH = Nothing End Function

Now call SynchronousShell, and pass it the entire compression script.

I believe the only way for this process to work is if CreateFromDirectory is executed in the same session as Add-Type.

So, we must pass the whole thing as 1 string. That is, load all 4 commands into a single sCmd variable, so that Add-Type remains associated with the subsequent CreateFromDirectory. In PowerShell syntax, you can separate them with ;

https://thomas.vanhoutte.be/miniblog/execute-multiple-powershell-commands-on-one-line/

Also, you'll want to use single-quotes instead of double-quotes, else double quotes around the strings are removed when the daisy-chained commands are passed to powershell.exe

http://stackoverflow.com/a/39801732/209942

sCmd = "ps4 Add-Type -AssemblyName System.IO.Compression; $src = 'C:\Users\v1453957\documents\Experiment\rezip\aFolder'; $zip='C:\Users\v1453957\Documents\Experiment\rezip\my.zip'; [io.compression.zipfile]::CreateFromDirectory($src, $zip)"

Solved. The above constitutes the complete solution.


Extra info: Additional comments below are for special circumstances:

Multi-version .Net environments

If a .NET < 4.0 is the active environment on your OS, then System.IO.Compression does not exist-- the Add-Type command will fail. But if your machine has the .NET 4 assemblies available, you can still do this:

  • Create a batch file which runs PowerShell with .Net 4. See http://stackoverflow.com/a/31279372

  • In your Add-Type command above, use the exact path to the .Net 4 Compression assembly. On my Win Server 2008:

Add-Type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.IO.Compression.FileSystem\v4.0_4.0.0.0__b77a5c561934e089\System.IO.Compression.FileSystem.dll"

Portability

Turns out, on my machine, I can copy the compression dll to any folder, and make calls to the copy and it works:

Add-Type -Path "C:\MyFunnyFolder\System.IO.Compression.FileSystem.dll"

I don't know what's required to ensure this works-- it might require the full .Net 4.0 or 2.0 files to be located in their expected directories. I assume the dll makes calls to other .Net assemblies. Maybe we just got lucky with this one :)

Character Limit

Depending on the depth of our paths and filenames, character-count may be a concern. PowerShell may have a 260-character limit (not sure).

https://support.microsoft.com/en-us/kb/830473

https://social.technet.microsoft.com/Forums/windowsserver/en-US/f895d766-5ffb-483f-97bc-19ac446da9f8/powershell-command-size-limit?forum=winserverpowershell

Since .Run goes through the Windows shell, you also have to worry about that character limit, but at 8k+, it's a bit roomier: https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553 http://stackoverflow.com/a/3205048/209942

Site below offers a 24k+ character method, but i've not studied it yet: http://itproctology.blogspot.com/2013/06/handling-freakishly-long-strings-from.html

At minimum, since we can put the dll wherever we like, we can put it in a folder near C: root-- keeping our character-count down.

Update: This post shows how we can put the whole thing in a script-file, and call it with ps4.cmd. This may become my preferred answer:

.\ps4.cmd GC .\zipper.ps1 | IEX

-- depending on answer here.


CopyHere:

Re the question: can CopyHere command execute on command-line?

CopyHere can be executed directly at PowerShell prompt (code below). However, even in powershell it's asynchronous-- control returns to PowerShell prompt before the process is finished. Therefore, no solution for the OP. Here's how it's done:

> $shellapp=new-object -com shell.application
> $zippath="test.zip"
> $zipobj=$shellapp.namespace((Get-Location).Path + "\$zippath")
> $srcpath="src"
> $srcobj=$shellapp.namespace((Get-Location).Path + "\$srcpath")
> $zipobj.Copyhere($srcobj.items())

这篇关于如何在Windows上的1个语句中压缩文件夹内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆