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

Hi, Hopefully an easy one ... I can't get the system () function to change directory on a Windows 2000 machine. Code snippet is as follows:
system('cd C:\temp') or die "ERROR!"; system("del *.audit");
Basically, it gives me "ERROR!" every time. Any help greatly appreciated.

Replies are listed 'Best First'.
Re: Problem with system ()
by Corion (Patriarch) on Mar 18, 2003 at 14:18 UTC

    There are three problems with your code :

    First, system() returns 0 on success and non-zero on failure, so the method to use it is :

    system "..." and die "Error: $! / $?";

    The cd thing you enter on the command line is no external command but a built-in command of cmd.exe. Thus you would have to do system($ENV{COMSPEC},'/c cd c:\temp').

    Using shell commands to set the current directory is not guaranteed to influence the current working directory of the parent. You want to use the CwdFile::chdir module to change the directory and the unlink() function to erase files.

    Update: Changed the module name
    perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web

      A few corrections:

      1. 'system() and die()' is not very easy to parse. I strongly suggest "system()==0 or die" or even "if (system()!=0) { die }".
      2. system() does invoke cmd.exe on WINNT, just as it invokes command.com on WIN9x and /bin/sh on UNIX. There is no need to $ENV{'COMSPEC'}.
      3. You say "using shell commands to set the current directory is not guaranteed to influence the current working directory of the parent." In fact, chdir() system calls in a sub-process do not affect the current working directory of the parent at all on UNIX or WIN32. You really want to use chdir() directly, as "chdir($dir) or die".
Re: Problem with system ()
by MZSanford (Curate) on Mar 18, 2003 at 14:20 UTC

    You may want to use cwd in your program, then use system(). Beware of the fact that on most systems (and i suspect Win32 too) system() opens a new shell for each call. So, your first would chage directory and then close, and your second would delete files in the current directory.

    As a side note, if you are deleteing files you may want to look at unlink, which will let you check the status of your deletes. (glob + unlink = gooood)


    from the frivolous to the serious
Re: Problem with system ()
by hardburn (Abbot) on Mar 18, 2003 at 14:52 UTC

    I find that when most people try to execute outside programs from Perl, a pure-Perl version is often as short or shorter. This is not to say there is no good use of executing outside programs, just that it's usually not.

    In this case, as another poster mentioned, you can use glob and unlink:

    unlink glob('C:\temp\*.audit');

    So it shaved off a line of code and doesn't have the overhead or potential security risk of executing an outside program. I don't see a downside here, except maybe the time it took to RTM.

    Update: Everyone needs to RTM. And that includes me. Problem with character escaping fixed, as per suggestions below.

    ----
    Reinvent a rounder wheel.

    Note: All code is untested, unless otherwise stated

      This has a problem with backslashes:
      unlink glob("C:\temp\*.audit");
      Should be one of these:
      unlink glob("C:\\temp\\*.audit"); unlink glob('C:\temp\*.audit'); unlink glob("C:/temp/*.audit");

      Careful -- you may get errors with this. You either need to single quote strings with \ or double your \. So you can change this to "C:\\temp\\*.audit" or 'C:\temp\*.audit' or (my preference) "C:/temp/*.audit"

Re: Problem with system ()
by Steve_p (Priest) on Mar 18, 2003 at 16:00 UTC

    Well, system returns zero unless the program completely fails to start when it returns a -1. So, zero would only be returned if successful, meaning that die will be called. You could always call the Perl function chdir instead of calling system, or you could call system like

    system("cd C:\temp") == 0 or die "Error!:  $?";

    If you choose the chdir route remember that if you want to keep what you have, the "\" character is the escape character, so Perl will interpret this as "C:<tab character>temp". You have two choices. The first is escape the backslash so it will look like

    chdir "C:\\temp" or die "ERROR!: $!";

    or ActiveState will accept "/" as the directory separator, so you could use

    chdir "C:/temp" or die "ERROR!:  $!";
      All, as ever, many thanks for the responses. I have now fixed the problem. The code I have chosen is as follows:
      chdir "C:\\Program Files\\Analysis\\Auditfiles" or die "ERROR!: $!"; system ("del *.audit");
      I tried to install File::Chdir but unfortunately received a whole load of errors during nmake test that I couldn't fathom. This was a non-starter. Also, I tried using unlink and glob together, but while this compiled, it didn't actually delete anything. I realise chdir is bit crude, but the script I am using it in is very simple. Thanks again!
Re: Problem with system ()
by BrowserUk (Patriarch) on Mar 18, 2003 at 14:15 UTC

    Update:

    Corion is correct. Whilst the method I show below will probably achieve your aim, the reasoning is wrong.

    The main problem with your original code, ie. The reason your are getting 'ERROR' all the time, is because system returns the return code from the command run, rather than success or failure, therefore the idoim system(...) or die "ERROR"; is wrong.

    For more information on how to test for the success or failure of a command issued with system follow that link or look it up in your local copy of perlfunc.

    However, the command you were issuing wouldn't have achieved your aim without the /d switch corion describes, and which I always forget about.

    </update>

    The standard cd command on Win32 doesn't accept a drive component to the command. (Try that same command from the command line to verify this).

    You could use system( 'c: & cd \temp' );


    Examine what is said, not who speaks.
    1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
    2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
    3) Any sufficiently advanced technology is indistinguishable from magic.
    Arthur C. Clarke.

      Sorry, but that's wrong. At least under NT4 (and higher), the following behaviour holds :

      S:\Systeme_Projekte\Ism\Maischein>cd S:\Systeme_Projekte\Ism\Maischein S:\Systeme_Projekte\Ism\Maischein>cd c: C:\TEMP S:\Systeme_Projekte\Ism\Maischein>cd c:\winnt S:\Systeme_Projekte\Ism\Maischein>cd S:\Systeme_Projekte\Ism\Maischein S:\Systeme_Projekte\Ism\Maischein>cd c: C:\WINNT S:\Systeme_Projekte\Ism\Maischein>cd /d c:\winnt C:\WINNT>

      The cd command without any parameter outputs the "current directory".

      The cd drive: command outputs the "current directory of the drive".

      The cd drive:\path command changes the "current directory of the drive".

      The /d option also changes the current drive.

      perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web