为什么通过Perl系统调用和psexec触发的Windows XCOPY会失败?

4
我正在使用psexec在远程Windows机器上启动Perl程序。该程序对xcopy进行系统调用。当直接在机器上运行时,这很好用,但是通过psexec远程运行时,xcopy会失败,并显示以下消息:
文件创建错误-函数不正确。
(根据用户,消息可能改为“拒绝访问”。)
请注意,$!给出以下诊断:
syscall.pl处的坏文件描述符。 Perl以错误代码9退出。
无论是通过system()还是反引号调用xcopy似乎都没有区别。
我应该指出,“from”文件夹是ClearCase动态视图(M驱动器)。
奇怪的是,当直接从psexec调用xcopy时,它似乎能正常工作。
以下是一些其他奇怪之处:
1. xcopy并不总是失败。某些文件似乎只是“被诅咒”的。只读属性似乎不是因素。
2. 一旦复制成功(例如通过Windows资源管理器),诅咒就会解除,那个特定的文件将不再导致xcopy错误。
3. 问题似乎不在目标文件夹中。一旦解除了诅咒,文件可以复制到新目标。
以下是我用于缩小问题范围的测试Perl脚本的一部分(文件夹名称已经通用化)。请注意,对于每个“my $cmd”进行测试,我都注释掉了以前的命令,并添加了状态注释。
# ClearCase directory M:\STUFF\ABC contains ABC.tst, ABC.zip and several nonempty subfolders

# Directory copy, D drive to D drive
#my $cmd = "xcopy D:\\temp\\src D:\\temp\\dest /e /i /y";
# works

# Directory copy, M drive to D drive
#my $cmd = "xcopy M:\\STUFF\\ABC D:\\temp\\dest /e /i /k /y";
# fails with "File creation error - Incorrect function" or "Access denied"

# File copy (.tst), M drive to D drive (trailing backslash)
#my $cmd = "xcopy M:\\STUFF\\ABC\\ABC.tst D:\\temp\\dest\\";
# works!

# Directory copy, M drive to D drive (trailing backslash)
#my $cmd = "xcopy M:\\STUFF\\ABC D:\\temp\\dest\\ /e /i /k /y";
# copies the .tst file, but fails on the .zip (yes, the .tst file is now getting copied)

# Directory copy, M drive to D drive (same as above but without trailing backslash)
#my $cmd = "xcopy M:\\STUFF\\ABC D:\\temp\\dest /e /i /k /y";
# copies the .tst file, but fails on the .zip

# File copy (.zip), M drive to D drive
#my $cmd = "xcopy M:\\STUFF\\ABC\\ABC.zip D:\\temp\\dest";
# fails 

# File copy (.zip), M drive to D drive (trailing backslash)
#my $cmd = "xcopy M:\\STUFF\\ABC\\ABC.zip D:\\temp\\dest\\";
# fails

# After manually (Windows Explorer) copying the .zip file to the dest folder and deleting it  
# Directory copy, M drive to D drive with /c (continue after failure)
#my $cmd = "xcopy M:\\STUFF\\ABC D:\\temp\\dest /c /i /e";
# copies the .tst and .zip file (!), but fails on all other files (folders were successfully created)

# After manually copying the Folder1 folder to the dest folder and then deleting it  
#my $cmd = "xcopy M:\\STUFF\\ABC D:\\temp\\dest /c /i /e";
# copies the .tst and .zip file and the contents of Folder1(!), but fails on all other files

# Different dest:
my $cmd = "xcopy M:\\STUFF\\ABC D:\\temp\\dest1 /c /i /e";
# Same results as immediately above

print "Executing system command: $cmd ...\n";
system ($cmd);
#print(`$cmd 2>&1`); #same

2
我猜测ClearCase动态视图是真正的罪魁祸首。我会尝试在常规文件系统上运行相同的代码以排除这种可能性。 - msw
2
顺便提一下:在这里使用单引号可以节省很多反斜杠。 - reinierpost
尝试使用 system ($cmd) or die "'$cmd' failed: $!"; 这个命令,它会告诉你错误信息(如果存在的话),我认为这比你的打印命令更可靠。 - Barton Chittenden
1
@BartonChittenden 如果system成功,它只会打印$!。相反,使用system ($cmd) and die "'$cmd' failed: $!"。有关详细信息,请参见perldoc -f system。另外,使用我建议的LIST形式。 - Sinan Ünür
@SinanÜnür -- 噢,你说得完全正确,我在系统命令的逻辑上犯了错误。我一直专注于使用 $!,却忽略了这一点。 - Barton Chittenden
显示剩余4条评论
5个回答

3
建议使用Perl自身进行复制,而不是使用xcopy命令。有一个模块File::Copy::Recursive非常简单易用。它不是Perl标准发行版的一部分,因此您需要使用cpan来安装它。
如果您不想使用非本地模块,则可以尝试使用File::Find查找目录中的文件,然后将其与File::Copy结合使用。
Perl Monks上找到了两个示例,一个使用组合,另一个使用File::Copy::Recursive
是的,这并没有直接回答您的问题,但您应该尽可能避免使用system命令。当您与系统的shell和命令处理器交互时(特别是当ClearCase通过文件系统进行操作时),您可能会遇到许多意外的交互,这可能会导致在某些情况下工作,在其他情况下不起作用。
要找出使用system调用时出现的问题,您必须假设错误可能在ClearCase、cmd.exe shell、xcopy命令或Perl中。通过不使用system命令,您已简化了问题,并且很多时候实际上加速了整个过程。

在我们公司获得部署File::Copy::Recursive模块的许可之前,我们可能不得不使用File::Find/File::Copy组合。但我希望我们可以解决这个奇怪的xcopy问题,而不必诉诸于此。 - UhClem
@UhClem:编写一个File::Find/File::Copy的包装子程序应该不会太难。这样,你只需要将所有的系统调用转换为使用递归复制例程即可。至少可以尝试使用File::Copy来查看它是否能够在xcopy失败的情况下正常工作。File::Copy的另一个优点是,你可以使用正斜杠作为路径分隔符,从而消除了转义反斜杠的需要。仅此一项就足以让我感到非常满意了。 - Barton Chittenden

3
尝试将输入重定向到空设备以查看xcopy是否正常工作。我不知道为什么,很久以前我遇到过这个问题,但通过搜索(可能是通过网络搜索)我找到了解决方法。
它看起来应该像这样: xcopy /参数 $源 $目标 <nul:;
(整个命令周围的反引号没有显示)
JKE

谢谢,你真是救星。我遇到了与UniVerse EXECUTE "DOS /C XCOPY ..."相同的问题,它在子程序中可以工作,但在幻影进程中调用时没有任何输出。在末尾添加重定向解决了这个问题。 - DarthJDG

2

你尝试过使用参数列表的system函数吗?

my @args= qw(M:\STUFF\ABC D:\temp\dest1 /c /i /e);
warn "Executing 'xcopy @args' ...\n";
system xcopy => @args;

想过尝试这个,还有open()函数,但是由于反引号和system函数给出了相同的结果,并且问题在手动复制后始终“自行修复”,我认为系统调用的类型并不是罪魁祸首。 - UhClem

1

File::Copy::Recursive 可能会解决您的问题。

这样,您就可以更加控制地复制文件。您可以将此调用包装到 eval 块中,并在其中应用某种重试逻辑?

也许对有问题的文件进行“虚拟更新”会有所帮助?更改文件的属性/时间戳或将其重命名为其他名称,然后再将其重命名回来...


是的,我在6个月前的代码中放了一个注释,说明File::Copy::Recursive是更好的选择。xcopy的系统调用只是一个临时解决方案,直到我们公司的人被说服允许使用那个模块。(长话短说。) - UhClem

1

如果您查看ClearCase如何管理Vob和视图的读/写访问权限, 您会发现以下区别:

  • 读取目录内容(需要r-x
  • 在目录中读取文件(仅需要目录上的'--x'和文件上的"r--")

因此,根据启动xcopy的进程背后的用户,您可能无法读取/访问某些目录/文件。
并且您需要确保用户已将CLEARCASE_PRIMARY_GROUP设置为正确的值(即列为vob主要组或其辅助组之一的组),以便访问Vob(但如果其保护足够宽松,则任何人都可以访问它)。

所有这些都适用于动态视图,并且仅适用于快照视图的更新(一旦更新完成,您将获得本地复制的文件,任何进程都可以读取该文件)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接