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

Hi Experts, I am using a subroutine to first delete one file and then copy one file to another. below is the snippet of the code.
use file::copy qw(copy); deleteTemp($tempfile,$serverlist); sub deleteTemp { my ($delTemp,$serverlist)=@_; unlink $deltemp; #copy($serverlist,$deltemp); }
But the program never enters into this subroutine while execution. The two file names which i am passing are simple text files.

Replies are listed 'Best First'.
Re: Subroutine to delete one file and copy one file to another
by stevieb (Canon) on Jul 20, 2015 at 20:27 UTC

    You should be using use strict; and use warnings;, and that would have caught you have a typo in one of your variables ($delTemp) in the unlink().

    You should also check if the file exists, and then report back if unlink() failed...

    if (-f $delTemp){ unlink $delTemp or die "can't delete >$delTemp< file: $!"; }

    To further, you should check the copy() was successful as well:

    my $a = 'a.txt'; my $b = 'b.txt'; copy $a, $b or die "can't copy $a to $b: $!"; die "can't find copied file $b: $!" unless -f $b;

    -stevieb

      if (-f $delTemp){ unlink $delTemp or die "can't delete >$delTemp< file: $!"; }

      This opens a door for a race condition (https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use): Someone can change the file $delTemp between the stat call inside -f and the unlink call.

      The clean way to handle this: Just call unlink. Yes, unlink will fail when $delTemp does not exist. Check $! before calling die. POSIX specifies that unlink will return ENOENT if and only if the file was not found.

      Something like the following should do the trick, using %! (available since Perl 5.005):

      unlink $delTemp or $!{ENOENT} or die "can't delete '$delTemp': $!";

      See also Re^2: Subroutine to delete one file and copy one file to another.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      Hi , I will show you my complete code , which after using strict and warnings is not getting complied.
      #!C:\Perl\bin\perl.exe use Win32; use IO::Handle; use File::Find; use strict; use warnings; use File::Copy qw(copy); ###################################################################### +############## #This script is created to put the servers in unplanned outage as part + of the tasks# #that are received to stop the monitoring on the servers #due to some maintenance activity on the servers. # # # #expect values 'on|off' ($SEC,$MIN,$HOUR, $DAY, $MON, $YEAR)= (localtime) [0..6]; $year=$YEAR+1900; $month=$MON+1; $day=$DAY; $date="$year\_$month\_$day"; $LOG="E:/Temp/inyrohs/maintenanceMode_$date.log"; my $tempfile="E:/Temp/inyrohs/temp/outagenodes_temp.txt"; my $serverlist="E:/Temp/INYROHS/serverlist.txt"; open( MYFILE, "<E:/Temp/INYROHS/serverlist.txt"); @outagenodes=<MYFILE>; close MYFILE; open ( MYTEMPLIST, "<E:/Temp/INYROHS/temp/outagenodes_temp.txt"); @tempnodes=<MYTEMPLIST>; close MYTEMPLIST; print "@outagenodes\n"; #Below subroutine call is to delete the temp file if already existing +so that the outage is not run on the previously entered servers. deleteTemp($tempfile,$serverlist); open (LOG,">> $LOG") or die "Can't open $LOG file: $!\n"; printf LOG ("\nTime is %02d:%02d:%02d.\nStarting the maintenance mode +process to turn $maintMode outages.\n\n", $HOUR, $MIN, $SEC); print "$date \n"; print "starting the process \n"; my $maintMode=$ARGV[0]; chomp($maintmode); print "maintMode: $maintMode:\n"; foreach $NODES (@tempnodes) { printLog("Node: $NODES, processing...\n"); if($maintMode =~ m/on/) { print "entered loop \n"; $cmd="ovownodeutil -outage_node -unplanned -disable_heartb +eat -delete_msgs -node_name $NODES -$maintMode "; print "$cmd\n"; system($cmd); print "done with command"; } elsif($maintMode=~ m/off/) { printLog("Putting the server $NODES in the outage.\n"); $cmd="ovownodeutil.cmd -outage_node -unplanned -disable_he +artbeat -delete_msgs -node_name $NODES -$maintMode"; #$cmdstopopcmona="ovdeploy -cmd "ovc -stop opcmona" -host +$NODES"; #$cmdstopopcle="ovdeploy -cmd "ovc -start opcle" -host $NO +DES"; #$cmdstartopcmona="ovdeploy -cmd "ovc -start opcmona" -hos +t $NODES"; #$cmdstartopcle="ovdeploy -cmd "ovc -start opcle" -host $N +ODES"; # $cmdstart="opcragt -start $NODES"; print "$cmd\n"; system($cmd); # print "$cmdstopopcmona\n"; # # system($cmdstopopcmona); # # print "$cmdstopopcle"; # system($cmdstopopcle); # print "$cmdstartopcle\n"; # system($cmdstartopcle); # #print "$cmdstart\n"; # system($cmd); # system($start); } } sub printLog { my ($logLine) = @_; chomp($logLine); $logLine=$logLine . "\n"; print "$logLine"; print LOG "$logLine"; } sub deletTemp { my ($delTemp,$serverlist)=@_; print "temp: $delTemp\n"; print "serv: $serverlist\n"; unlink $delTemp; copy($serverlist,$delTemp); }
      And below are the errors:
      E:\Temp\inyrohs>perl outage_nodes.pl on Possible unintended interpolation of @outagenodes in string at outage_ +nodes.pl line 48. Variable "$LOG" is not imported at outage_nodes.pl line 59. Global symbol "$SEC" requires explicit package name at outage_nodes.pl + line 20. Global symbol "$MIN" requires explicit package name at outage_nodes.pl + line 20. Global symbol "$HOUR" requires explicit package name at outage_nodes.p +l line 20. Global symbol "$DAY" requires explicit package name at outage_nodes.pl + line 20. Global symbol "$MON" requires explicit package name at outage_nodes.pl + line 20. Global symbol "$YEAR" requires explicit package name at outage_nodes.p +l line 20. Global symbol "$year" requires explicit package name at outage_nodes.p +l line 22. Global symbol "$YEAR" requires explicit package name at outage_nodes.p +l line 22. Global symbol "$month" requires explicit package name at outage_nodes. +pl line 23. Global symbol "$MON" requires explicit package name at outage_nodes.pl + line 23. Global symbol "$day" requires explicit package name at outage_nodes.pl + line 24. Global symbol "$DAY" requires explicit package name at outage_nodes.pl + line 24. Global symbol "$date" requires explicit package name at outage_nodes.p +l line 25. Global symbol "$year" requires explicit package name at outage_nodes.p +l line 25. Global symbol "$month" requires explicit package name at outage_nodes. +pl line 25. Global symbol "$day" requires explicit package name at outage_nodes.pl + line 25. Global symbol "$LOG" requires explicit package name at outage_nodes.pl + line 27. Global symbol "$date" requires explicit package name at outage_nodes.p +l line 27. Global symbol "@outagenodes" requires explicit package name at outage_ +nodes.pl line 37. Global symbol "@tempnodes" requires explicit package name at outage_no +des.pl line 44. Global symbol "@outagenodes" requires explicit package name at outage_ +nodes.pl line 48. Global symbol "$LOG" requires explicit package name at outage_nodes.pl + line 59. Global symbol "$LOG" requires explicit package name at outage_nodes.pl + line 59. Global symbol "$maintMode" requires explicit package name at outage_no +des.pl line 61. Global symbol "$HOUR" requires explicit package name at outage_nodes.p +l line 61. Global symbol "$MIN" requires explicit package name at outage_nodes.pl + line 61. Global symbol "$SEC" requires explicit package name at outage_nodes.pl + line 61. Global symbol "$date" requires explicit package name at outage_nodes.p +l line 63. Global symbol "$maintmode" requires explicit package name at outage_no +des.pl line 71. Global symbol "$NODES" requires explicit package name at outage_nodes. +pl line 76. Global symbol "@tempnodes" requires explicit package name at outage_no +des.pl line 76. Global symbol "$NODES" requires explicit package name at outage_nodes. +pl line 83. Global symbol "$cmd" requires explicit package name at outage_nodes.pl + line 95. Global symbol "$NODES" requires explicit package name at outage_nodes. +pl line 95. Global symbol "$cmd" requires explicit package name at outage_nodes.pl + line 97. Global symbol "$cmd" requires explicit package name at outage_nodes.pl + line 99. Global symbol "$NODES" requires explicit package name at outage_nodes. +pl line 112. Global symbol "$cmd" requires explicit package name at outage_nodes.pl + line 114. Global symbol "$NODES" requires explicit package name at outage_nodes. +pl line 114. Global symbol "$cmd" requires explicit package name at outage_nodes.pl + line 128. Global symbol "$cmd" requires explicit package name at outage_nodes.pl + line 131. Execution of outage_nodes.pl aborted due to compilation errors.
        Hi shroh,

        You can add 'my' to the variable declarations wherever the error messages indicates.

        Then you will get an appropriate error message about undefined subroutine because 'e' is missing in your sub name.

        ... sub deletTemp { ...
      Hi Stieve, May be i am stuck up in here: What i want is stated below: 1) If i dont have the temp file, it should create a new file and copy the contents of serverlist(already existing) into the newly created temp file. 2) If the file is already existing, it should delete the temp file and again copy the contents of that file into the temp file. All i want this to be done in the subroutine.

        Without trying the whole of your main program, it looks like the other monks have the delete subroutine correct--so the main program I think is the problem. Speaking for myself, I would start by cleaning up that ugly code and rename some varables variables to help make it clear what I am tring trying to do. Some good comments would help, too.

Re: Subroutine to delete one file and copy one file to another
by pme (Monsignor) on Jul 20, 2015 at 19:56 UTC
    Hi shroh,

    This little piece of code successfully calls deleteTemp().

    use strict; # always use 'strictures' use warnings; use File::Copy qw/ copy /; # perl module names are case sensitiv +e my $tempfile = "tempfile.txt"; my $serverlist = "serverlist.txt"; deleteTemp($tempfile, $serverlist); sub deleteTemp { my ($delTemp, $serverlist) = @_; print "temp: $delTemp\n"; print "serv: $serverlist\n"; unlink $delTemp; copy($serverlist,$delTemp); }
    UPDATE: Typos fixed. Thanks poj
Re: Subroutine to delete one file and copy one file to another
by Monk::Thomas (Friar) on Jul 21, 2015 at 11:22 UTC

    I would add a safeguard:

    if (not -d $delTemp) { unlink $delTemp; } else { warn "Whoaaaah. Hold it. $delTemp is a directory!\n"; }

    Ideally this would never trigger, but if it does you have prevented some serious trouble:

    unlink will not attempt to delete directories unless you are superuser and the -U flag is supplied to Perl. Even if these conditions are met, be warned that unlinking a directory can inflict damage on your filesystem. Finally, using unlink on directories is not supported on many operating systems. Use rmdir instead.
    -- http://perldoc.perl.org/functions/unlink.html

      if (not -d $delTemp) { unlink $delTemp; }

      Unfortunately, this opens the door for race conditions (see https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use). The documentation that you cited shows that it is save to use unlink even on directories. It will simply fail. You won't notice, though, because you don't check for errors here. (Just add or die "Can't unlink $delTemp: $!" after unlink $delTemp).

      There is only one way to make unlink dangerous, and all of the following must happpen:

      1. use an operating system that allows unlinking a directory
      2. be root
      3. start the script with perl -U script.pl or change the shebang line to include the -U flag

      A quick search shows that Linux does not unlink directories, but returns EISDIR instead. FreeBSD behaves the same. OpenBSD seems to allow unlinking directories, depending on the filesystem, but only for root. NetBSD seems to behave like OpenBSD.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)