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

For years I have daily downloaded a number of cryptic crossword puzzles into a directory called Xwords, with file names ranging from 5 to 7 alphanumeric characters e.g., 2107.puz
This directory also contains other files besides the .puz files used for processing
After I collected a number of crosswords, and have processed the files, I remove them to a subdirectorty called Printed_Xwords, leaving Xwords directory ready for the next batch

Unfortunately, these same filenames recurr each year, so the latest filenames have to be changed before they are moved. This is usually done manually, by adding an alphabetic letter A. ie., 2107A.puz

Can anyone assist with a script which would go into the Xword directory, rename the files as above, then move them to the subdirectory Printed_Xwords
My regards,
Joe,
Townsville, Australia

Edit: chipmunk 2001-07-11

  • Comment on File renaming and removal to a subdirectory

Replies are listed 'Best First'.
Re: File remaing and removal to a subdirectory
by bwana147 (Pilgrim) on Jul 11, 2001 at 16:45 UTC

    I'm sliding off-topic, but the shell should suffice for that kind of little task:

    % cd Xwords % for fn in *.puz; do mv $fn Printed_Xwords/${fn%.puz}A.puz; done

    HTH

    --bwana147

Re: File renaming and removal to a subdirectory
by Abigail (Deacon) on Jul 11, 2001 at 17:30 UTC
    # Subroutine that takes a filename of the form DDMM.xxx and returns # a filename of the form DDMMYYYY.xxx. sub alt_name { local $_ = shift; die "Illegal filename" unless my ($dd, $mm, $ext) = /^(\d\d)(\d\d) +.(.+)$/; my ($day, $month, $year) = (localtime) [3 .. 5]; $year += 1900; $month ++; if ($mm > $month || $mm == $month && $dd > $day) { # Assume file dates from last year. $year --; } sprintf "%02d%02d%04d.%s" => $dd, $mm, $year, $ext; }
    As you see, the code doesn't add an 'A' or some other letter, but adds a year. It assumes that a date with a later month than the current month, or with a later day if the month is the current month is a file from last year (probably mostly important during your first run in January), else it's a file from this year. Of course, this scheme will fail if you wait more than a year between batches. But if you do, files in your Xwords directory might be overwritten anyway.

    The reason I picked a year and not a letter is that otherwise you need to keep some state to see when you need to switch to the next letter. Now we just borrow the state from your OSses clock.

    -- Abigail

Re: File remaing and removal to a subdirectory
by jeroenes (Priest) on Jul 11, 2001 at 16:47 UTC
    Sure we can assist, and we even like to do so. What problems do you exactly have with the code? If you show us what you have thusfar, we can give more specific help.

    Without code, I can give you pointers to docs that will probably help you on the road: perlre, rename, glob and perlop.

    Cheers,

    Jeroen

      Thanks for your offer.
      At this stage I am a newby ( raw initiate) who does not know where to start, but is ploughing his way through a text and doing basic tutorial exercises.
      Any assistance would be greatly appreciated
      Joe, Townsville
        When I started with perl, I greatly enjoyed 'Learning Perl' from Randall L. Schwartz aka merlyn. Great book for the first steps in perl.

        There are two things that you need to do here. First, create a list of files you want to move around. That's done by globbing: @list = <*.puz>.

        Than you'll have to rename that file to add the 'A' and move it at the same time, the first part is done by regexes, see perlre: s/(\.puz)$/A$1/; and the second by concatenation: $name= 'subdir/' . $name.

        You'll have to rename every file, so loop over your list with for:

        for my $new ( <*.puz> ){ my $old = $new; $new =~ s/(\.puz)$/A$1/; $new = 'subdir/' . $new; rename $old, $new; }

        Updated

Re: File remaing and removal to a subdirectory
by tachyon (Chancellor) on Jul 11, 2001 at 16:56 UTC

    Hi Joes this snippet will rename all the files in a directory that end in $from so they end in $to for you. As every operating system has some form of copy a -> b function using it will be the quickest way to achieve the second part of your goal.

    my $dir = "c:/"; my $from = '.puz'; my $to = 'A.puz'; while (<$dir*$from>) { my ($old,$new); $old = $new = $_; $new =~ s/$from$/$to/; rename $old, $new; }

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

      Sorry tachyon, your script to change file names did not work.

      I have enclosed the actual script used FYI

      It went right through and printed the last line, but the files in C:/ Xwords had not changed their name.

      #!/usr/bin/perl -w use strict; my $dir = "c:/Xwords"; my $from = '.puz'; my $to = 'A.puz'; while (<$dir*$from>) { my ($old,$new); $old = $new = $_; $new =~ s/$from$/$to/; rename $old, $new; } print "Files renamed !";

        The problem is that you have forgotten the / after Xwords. It works fine if you add it.

        my $dir = "c:/Xwords/"; my $from = '.puz'; my $to = 'A.puz'; while (<$dir*$from>) { my ($old,$new); $old = $new = $_; $new =~ s/$from$/$to/; rename $old, $new; }

        The problem is that the * in the glob is rather like *.* in something like copy *.* a: - if the * matched everything you would not need the . there. In a glob the * will match a filename like 'foo.bar' but not a path/filename like '/foo.bar' Without the trailing / the glob <$dir*$from> is trying to match files in c:/ that match the filename /Xwords\w*.puz/ If you put a file in C:/ called Xwordstest.puz it will get renamed. In the original script the $dir was 'c:/' not 'c:', that is the difference and the problem.

        A useful trick when a script does not work is to slip in some print statements to see what is going on. If you place a print in the while loop you will see that the while loop is never entered in your example.

        Good to see you back.

        cheers

        tachyon

        s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: File remaing and removal to a subdirectory
by MZSanford (Curate) on Jul 11, 2001 at 17:13 UTC
    Here is a quick thought (albeit untested):
    my $indir = "/tmp/a"; my $outdir = "/tmp/b"; my @suffix = qw(A B C D E F ); ## ON AND ON foreach my $sfilename (@infiles) { $ofilename = "$outdir/$sfilename"; if (-e $ofilename) { ## it exists, lets add a suffix for (@suffix) { if (! -e "$ofilename.$_") { rename "$indir/$sfilename","$ofilename.$_"; print "Renamed to $ofilename.$_"; exit(0); } else { ## suffix alreay taken, try next print "$ofilename.$_ exists ...\n"; } } } else { ## does not exist, rename rename "$indir/$sfilename", "$ofilename"; } }

    may the foo be with you
      albeit untested

      Indeed ;-) A couple of things go wrong. @infiles is not initialised, but I guess this is left as an exercise to the OP. No, I really wanted to point out the use of exit, which is not what you want here, IIUC. exit terminates the program however deep you are in nested blocs. By using exit here, you stop the program once the first file has been renamed with a suffix. Instead of that, you want to proceed to the next file.

      But next goes to the next iteration of the innermost loop. In our case, that would be the loop on suffices, not on files. So you'll need to use LABELS:
      FILE: foreach my $filename (@infiles) { ... if ( -e $ofilename ) { SUFFIX: for (@suffix) { if ( ! -e "$ofilename.$_" ) { ... next FILE; } else { ... } } } else { ... } }

      --bwana147