Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm making some changes to an existing script, ScriptA.pl, which copies a file and calls an external program to do some work on the copy. Everything is fine until the source file is read-only, a result of another automated program that creates it. One requirement of the project is to automate everything, so I need to patch ScriptA.pl to copy the source file and then strip off the read-only attribute if it exists.

Since everything is run off of WinXP machines, I thought the xcopy command would be the perfect tool for the job. As a test, I ran the following in the command prompt:

xcopy d:basedir\tools\ScriptA\bin\AS3.udb d:basedir\working3\build\ScriptA\ /r /y /i

Where basedir is my base directory. The command worked perfectly, but I can't seem to properly translate that into perl code. What I ended up with was the following line, choosing system in order to capture the return value:

system('xcopy', "\"$und_db\"", "\"$out_dir/\"", '/r', '/y', '/i')

$und_db is the file path of the source file, $out_dir is the destination for the copy. I get the following output on the screen:
Does d:/basedir/working3/build/ScriptA/ specify a file name or directo +ry name on the target (F = file, D = directory)? _d_ File creation error - Cannot create a file when that file already exis +ts.

I can't understand the file creation error since I deleted the existing copy to make sure it worked. I also don't understand why the prompt even appears, as the /i parameter should force xcopy to assume directory. Removing the / in the system call fixes the prompt issue, but the file fails to copy and xcopy doesn't give any errors to indicate why. Removing the quotes around the variable names gives the error "Invalid number of parameters". Clearly the problem is related to how I wrote the system call, but I'm totally stumped as to what the problem is. Any help is appreciated.

Replies are listed 'Best First'.
Re: Remove Read-Only using xcopy
by VinsWorldcom (Prior) on Jun 15, 2009 at 19:42 UTC
    I think your "$out_dir/" is causing problems as Windows (and thus xcopy) uses forward slash, not backward slash as the directory character.

    Try this - it worked for me:

    system('xcopy', "\"$und_db\"", "\"$out_dir\\\"", '/r', '/y', '/i');
      Thanks for the tip, but it still didn't copy the file =(. The only feedback I get is "0 File(s) copied" before the script continues to run (and crashes when it can't find the copied file). I debugged the two variables right before the function call and they contained the correct values. This one line of code is the only change to the script, which worked for weeks until the read-only problem showed up. Still stumped as to why it doesn't copy while the manually typed command does.
        Do you have a backslash between the drive letter and your first directory (d:\basedir ...). In your example, you don't and when I try xcopy without that slash, I get "0 files copied). When I put the slash in, it works.

        {C} > ls Directory of C:\Documents and Settings\Administrator\My Documents\tmp [.] [..] test.txt 1 File(s) 0 bytes {C} > xcopy "c:Documents and Settings\Administrator\My Documents\tmp\t +est.txt" "c:Documents and Settings\Administrator\My Documents\tmp\foo +\" File not found - test.txt 0 File(s) copied {C} > xcopy "c:\Documents and Settings\Administrator\My Documents\tmp\ +test.txt" "c:\Documents and Settings\Administrator\My Documents\tmp\f +oo\" C:\Documents and Settings\Administrator\My Documents\tmp\test.txt 1 File(s) copied {C} > ls Directory of C:\Documents and Settings\Administrator\My Documents\tmp [.] [..] [foo] test.txt 1 File(s) 0 bytes {C} > ls fool Directory of C:\Documents and Settings\Administrator\My Documents\tmp +\foo [.] [..] test.txt 1 File(s) 0 bytes
        Then using Perl:

        #!/usr/bin/perl use strict; my $und_db = "C:\\Documents and Settings\\Administrator\\My Documents\ +\tmp\\test.txt"; my $out_dir = "C:\\Documents and Settings\\Administrator\\My Documents +\\tmp\\foo"; system('xcopy', "\"$und_db\"", "\"$out_dir\\\"", '/r', '/y', '/i');
        And successful output:

        {C} > test C:\Documents and Settings\Administrator\My Documents\tmp\test.txt 1 File(s) copied {C} > ls foo Directory of C:\Documents and Settings\Administrator\My Documents\tmp +\foo [.] [..] test.txt 1 File(s) 0 bytes
Re: Remove Read-Only using xcopy
by Anonymous Monk on Jun 15, 2009 at 21:00 UTC
    Oops, caught a typo. The first line of code should read:

    xcopy d:\basedir\tools\ScriptA\bin\AS3.udb d:\basedir\working3\build\ScriptA\ /r /y /i
      $out_dir .= '\\' if substr($out_dir, -1) ne '\\'; system(xcopy => $und_dbm, $out_dir, '/r', '/y', '/i')

      Perl will add quotes around the args if necessary. Remember, the quotes aren't part of the argument, they are there to allow the shell to identify the arguments. That's not need when you use the multiple argument form of system since Perl uses the commas to identify the arguments.

Re: Remove Read-Only using xcopy
by Anonymous Monk on Jun 16, 2009 at 19:16 UTC
    Thanks for the input, but it still doesn't work. I did manage to find an alternative solution. Turns out that my company's windows machines have the chmod command (would've been nice to know that earlier). Using that in combination with File::copy works in every case.