运行Windows 7,当我将文件复制到外部磁盘时,在例行文件备份期间,我使用Powershell v2(从批处理文件运行) )在复制文件上重新创建原始文件的所有时间戳。
以下代码在大多数情况下都能成功运行,但并非总是如此: -
SET file=%1
SET dest=E:\
COPY /V /Y %file% "%dest%"
SetLocal EnableDelayedExpansion
FOR /F "usebackq delims==" %%A IN ('%file%') DO (
SET fpath=%%~dpA
SET fname=%%~nxA
)
PowerShell.exe (Get-Item \"%dest%\%fname%\").CreationTime=$(Get-Item \"%fpath%%fname%\" ^| Select-Object -ExpandProperty CreationTime ^| Get-Date -f \"MM-dd-yyyy HH:mm:ss\")
当我将源文件拖放到我的批处理文件中时,上面的代码会复制文件,然后将复制(目标)文件的创建日期/时间设置为源文件的创建日期/时间。
但是在某些情况下代码会失败。如果文件名包含“毒药”。字符,例如(例如)方括号 [...],它会给出错误" Property' CreationTime'无法在此对象上找到"。在“毒药”中解析文件名明显失败了。字符。
代码不会出现& 等符号错误。
我尝试了使用单引号和双引号转义Powershell命令的大量变体,但没有成功。请有人告诉我如何逃避Powershell反对的那些角色。
这只是一个更长的批处理例程的一小部分,我依赖它进行常规系统备份。我没有选择切换到.ps1文件,所以我需要一个在批处理文件中工作的解决方案,而不是.ps1文件。
感谢所有建议。
ADDENDUM :我找到了一个解决方案,采用了mklement0提供的一个建议。通过将以下命令替换为我原来的Powershell命令 -
,可以克服方括号的问题PowerShell.exe (Get-Item -LiteralPath \"%dest%\%fname%\").CreationTime=$(Get-Item -LiteralPath \"%fpath%%fname%\" ^| Select-Object -ExpandProperty CreationTime ^| Get-Date -f \"MM-dd-yyyy HH:mm:ss\")
为了将来参考,请注意(在 Windows 7 上):
使用此修订版命令可成功保留任何多余的空格字符。 不必须包含一对额外的双引号字符才能实现这一目标。
powershell.exe -command echo \"a b\"
会产生a b
将整个命令括在"..."
中有助于原则上 - powershell.exe -command "echo \"a b\""
- 但由于cmd.exe
然后没有将整个字符串识别为单个双引号字符串,元字符可以中断命令;例如,powershell.exe -command "echo \"a & b\""
不文件路径可以包含任何" (双引号)字符,因此不需要代码来转义该字符。双引号字符是FAT和NTFS文件系统中的非法字符,因此永远不会在文件的路径中遇到。
原则上使用' (单引号)在Powershell命令中,因为该字符在NTFS文件系统中不是非法的,所以可以在文件的实际路径中找到。必须首选使用双引号,因为双引号字符是非法的,在实际的NTFS路径中永远不会遇到。
使用ROBOCOPY,以下通配符解决方案成功甚至大多数毒性字符 - 除了!之外(即它可以应对 =&` ^ )。这个命令非常强大,如果有更多而不是一个毒药角色(虽然不是万无一失):
ROBOCOPY "%fpath% " "%dest%" "*%name%*%ext%*" /B /COPY:DAT /XJ /SL /R:0 /W:0 /V
一个。 "%fpath%"中的空格这是必不可少的,这不是错误。
湾在所有情况下唯一致命的毒药是EXCLAMATION MARK(!)。
℃。毒性字符似乎只是FILENAME中的问题,而不是路径中的问题。
答案 0 :(得分:2)
首先要做的事情:
在Windows 7中,您可以使用robocopy.exe
复制文件,默认情况下会保留时间戳(并且可以选择让您详细控制要复制的属性):
@echo off
:: Do NOT use setlocal ENABLEDELAYEDEXPANSION, because it would cause
:: misinterpretation of "!" chars. in filenames.
setlocal
:: Parse the file path given as %1 (the first argument) into its folder path and filename.
:: Be sure to pass the %1 argument *double-quoted* to prevent up-front interpretation
:: by cmd.exe; e.g.:
:: someBatchFile "c:\tmp\foo.txt" or someBatchFile "%file%"
:: Note that %~dp1 always returns a path with a trailing "\".
set "fpath=%~dp1"
set "fname=%~nx1"
:: Determine the destination folder
set "dest=E:\"
:: Use robocopy to copy the file to the destination dir. with timestamps preserved.
:: Syntax is: <source-dir> <dest-dir> <filename-or-wildcard> ...
:: IMPORTANT: To avoid problems with paths that end in "\", always follow
:: a variable reference inside "..." with a *space* (a trick discovered by
:: Ed999 himself).
robocopy "%fpath% " "%dest% " "%fname%"
注意:
虽然robocopy
主要用于复制整个目录,但 允许您通过从指定的通配符表达式复制单个文件第三个位置参数,如上面的"%fname%"
鉴于robocopy
- 与PowerShell不同 - 不考虑[
和]
通配符metacharacters,这种方法应该有效(如果您的文件名包含<,则只会出现问题< em>嵌入式 *
或?
字符,这不太可能。)
尾随空间技巧(例如"%fpath% "
)是必要的,因为robocopy
- 正如大多数命令行实用程序所做的那样 - 在结束时处理\"
参数为转义 "
,它打破了命令。严格地说,正确的解决方法是双重尾随\
(例如,"E:\\"
),但你可以通过附加空间来逃脱,因为路径中的任何尾随空格都被忽略。因此,使这些调用健壮的一种简单方法是在传递文件夹路径时始终使用"%var% "
(在结束双引号之前的尾随空格)。
robocopy
没有类似于/V
的开关,导致它验证文件是否已正确复制,但是 - 至少根据this blog post - 正在运行verify on
} beforehand应该具有相同的效果。
如果您仍需要通过PowerShell复制创建时间戳:
powershell -command "(Get-Item -LiteralPath '%dest%%fname%').CreationTime=(Get-Item -LiteralPath '%fpath%%fname%').CreationTime"
警告:如果您的文件名可能嵌入了'
个字符。 (单引号/撇号),你必须首先转义它们,通过加倍它们(例如,%fname:'=''%
返回%fname%
的全部值'
个实例翻倍):
powershell -command "(Get-Item -LiteralPath '%dest:'=''%%fname:'=''%').CreationTime=(Get-Item -LiteralPath '%fpath:'=''%%fname:'=''%').CreationTime"
请注意,整个命令都包含在"..."
中,以防止可能包含在变量值中的cmd.exe
元字符(例如&
)打破命令。 [1]
在命令字符串中,'...'
用于确保PowerShell将变量值视为 literals (如果您使用了{{例如,1}}和包含"..."
的文件名,结果将是意外的。)
$
确保-LiteralPath
将文件路径解释为文字,而它是
传递路径位置时隐含的Get-Item
参数,以及传递给
的路径
-Path
被解释为通配符表达式,这可能会导致PowerShell通配符元字符出现问题,例如-Path
和[
。
无需先将源文件的]
属性值转换为日期+时间字符串;您只需将其直接分配给目标文件的.CreationTime
属性,该属性的类型为.CreationTime
。
[1] 引用头疼:
在[System.DateTime]
中包含仅变量引用 - 转义双引号,如问题(\
),不是因为这样做会将值赋予空格规范化,这意味着多个空格的运行被规范化为单个空间。
虽然另外在\"%dest%\%fname%\"
中附加整个的命令原则上有帮助,但"..."
却无法识别整体string作为单个双引号字符串,在这种情况下,变量值中的cmd.exe
等元字符可以中断命令;例如,
&
工作得很好,忠实地保留了空白,但是
powershell.exe -command "echo \"a b\""
由于powershell.exe -command "echo \"a & b\""
而导致&
休息。
\""
代替\"
解决元字符问题,但重新引入空格规范化:powershell.exe -command "echo \""a & b\"""
收益a & b
因此,在整个'...'
字符串中使用"..."
PowerShell字符串是使命令健壮的最简单方法:您只需要在变量值中转义'
个实例
答案 1 :(得分:0)
归根结底,这是我在批处理文件中提出的最佳解决方案。
通过在Windows命令shell中使用REN和SET命令,它可以100%可靠地工作,它们没有ROBOCOPY或POWERSHELL的缺点。
@echo off
:: ** INPUT File **
SET file=%1
:: ** Destination Directory **
SET dest=C:\Users\%Username%\Desktop\test
:: ** Copy using Command Shell **
COPY /V /Y %file% "%dest%"
:: ** Location of PowerShell **
SET PowerShell=C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -NoProfile
:: ** Store PATH & NAME of source file (WITHOUT quotation marks) **
FOR /F "usebackq delims==" %%A IN ('%file%') DO (
SET fpath=%%~dpA
SET fname=%%~nxA
)
:: ** Rename the Files **
REN "%fpath%%fname%" temp1
REN "%dest%\%fname%" temp2
:: ** Set CREATED date of output file **
:: NB: Will fail if MONTHS (MM) is identical to MINUTES (mm)
%PowerShell% (Get-Item \""%dest%\temp2\"").CreationTime=$(Get-Item \""%fpath%temp1\"" ^| Select-Object -ExpandProperty CreationTime ^| Get-Date -f \"MM-dd-yyyy HH:mm:ss\")
:: ** Set MODIFIED date of output file **
:: NB: Will fail if MONTHS (MM) is identical to MINUTES (mm)
%PowerShell% (Get-Item \""%dest%\temp2\"").LastWriteTime=$(Get-Item \""%fpath%temp1\"" ^| Select-Object -ExpandProperty LastWriteTime ^| Get-Date -f \"MM-dd-yyyy HH:mm:ss\")
:: ** Wait **
:: No, I have no idea why it doesn't work without this...
echo. & echo Wait 15 Seconds ... & echo.
@CHOICE /T 15 /C yn /D y > NUL
:: ** Restore ORIGINAL filenames **
REN "%fpath%temp1" "%fname%"
REN "%dest%\temp2" "%fname%"
答案 2 :(得分:0)
不是通过篡改以前的答案来淹没水域,而是在这里找到我最新的&#39;&#39;关于这个问题。
一个月的额外摆弄导致我放弃了我以前的解决方案(效果很好!),其中不需要重命名文件(尽管我做了找到一个真正的整洁的想法:如果您采用我的简单解决方案,通过将文件重命名为任何无害的临时名称,无法运行任何有毒的元字符运行Powershell或Robocopy)。
但下面是一个从上述想法中剔除的解决方案,经过一个月的测试,它还没有引发任何失败。并且它没有采取任何&#34;未经授权的&#34;通过重命名文件的快捷方式!
以下批处理文件现在位于我的Windows 7发送到文件夹中,因此始终可以从Windows资源管理器右键单击菜单访问。
Windows 7&#34; send-to&#34;文件夹:
<强> C:\用户\%USERNAME%\应用程序数据\漫游\微软\的Windows \的SendTo 强>
@echo off
:: *** Copy file including its CREATED date & MODIFIED date ***
:: File : Drag-and-Drop
SET file=%1
:: Destination Directory
SET dest=E:\
:: ** Safety Checks **
ATTRIB -R -A -S -H %file%
:: ** Store PATH & NAME of file (WITHOUT quotation marks) **
FOR /F "usebackq delims==" %%A IN ('%file%') DO (
SET fpath=%%~dpA
SET fname=%%~nxA
SET name=%%~nA
SET ext=%%~xA
)
:: *** POWERSHELL : File only ***
:: ** Copy File **
COPY /V /Y %file% "%dest%"
:: ** Location of PowerShell **
SET PowerShell=C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -NoProfile
:: ** Set CREATED date of copy **
%PowerShell% -command "(Get-Item -LiteralPath '%dest:'=''%\%fname:'=''%').CreationTime=(Get-Item -LiteralPath '%fpath:'=''%%fname:'=''%').CreationTime"
:: ** Set MODIFIED date of copy **
%PowerShell% -command "(Get-Item -LiteralPath '%dest:'=''%\%fname:'=''%').LastWriteTime=(Get-Item -LiteralPath '%fpath:'=''%%fname:'=''%').LastWriteTime"
:: ** Set ACCESSED date of copy **
%PowerShell% -command "(Get-Item -LiteralPath '%dest:'=''%\%fname:'=''%').LastAccessTime=(Get-Item -LiteralPath '%fpath:'=''%%fname:'=''%').LastAccessTime"
:: Open Destination Directory
C:\Windows\Explorer.exe "%dest%"