Приглашаем посетить
Чарушин (charushin.lit-info.ru)

Section A.11.  Answers to Chapter 12 Exercises

Previous
Table of Contents
Next

A.11. Answers to Chapter 12 Exercises

  1. Here's one way to do it, with a glob:

        print "Which directory? (Default is your home directory) ";
        chomp(my $dir = <STDIN>);
        if ($dir =~ /^\s*$/) {         # A blank line
          chdir or die "Can't chdir to your home directory: $!";
        } else {
          chdir $dir or die "Can't chdir to '$dir': $!";
        }
        my @files = <*>;
        foreach (@files) {
          print "$_\n";
        }
    

    First, we show a prompt and read the desired directory, chomping it as needed. (Without a chomp, we'd be heading for a directory that ends in a newline, which is legal in Unix and therefore cannot be presumed to be extraneous by the chdir function.)

    If the directory name is nonempty, we'll change to that directory, aborting on a failure. If empty, the home directory is selected instead.

    Finally, a glob on "star" pulls up all the names in the (new) working directory, automatically sorted to alphabetical order, and the names are printed one at a time.

  2. Here's one way to do it:

        print "Which directory? (Default is your home directory) ";
        chomp(my $dir = <STDIN>);
        if ($dir =~ /^\s*$/) {         # A blank line
          chdir or die "Can't chdir to your home directory:
        $!";
        } else {
          chdir $dir or die "Can't chdir to '$dir': $!";
        }
        my @files = <.* *>;       ## now includes .*
        foreach (sort @files) {   ## now sorts
          print "$_\n";
        }
    

    Two differences from previous one: first, the glob now includes "dot star," which matches all the names that do begin with a dot. Second, we must sort the resulting list because some of the names that begin with a dot must be interleaved appropriately before or after the list of things without a beginning dot.

  3. Here's one way to do it:

        print "Which directory? (Default is your home directory) ";
        chomp(my $dir = <STDIN>);
        if ($dir =~ /^\s*$/) {         # A blank line
          chdir or die "Can't chdir to your home directory:
        $!";
        } else {
          chdir $dir or die "Can't chdir to '$dir': $!";
        }
        opendir DOT, "." or die "Can't opendir dot: $!";
        foreach (sort readdir DOT) {
          # next if /^\./; ##   if we were skipping dot files
          print "$_\n";
        }
    

    Again, here is the same structure as the previous two programs, but now we've chosen to open a directory handle. Once we've changed the working directory, we want to open the current directory, which is the DOT directory handle.

    Why DOT? If the user asks for an absolute directory name, like /etc, there's no problem opening it. But if the name is relative, like fred, watch what would happen. We chdir to fred and we want to use opendir to open it. That would open fred in the new directory, not fred in the original directory. The only name we can be sure will mean "the current directory" is ., which always has that meaning (on Unix and similar systems, at least).

    The readdir function pulls up all the names of the directory, which are sorted and displayed. If we had done the first exercise this way, we would have skipped over the dot files, and that's handled by uncommenting the commented-out line in the foreach loop.

    You may find yourself asking, "Why did we chdir first? You can use readdir and friends on any directory, not merely on the current directory." Primarily, we wanted to give the user the convenience of being able to get to her home directory with a single keystroke. But this could be the start of a general file management utility program. The next step might be to ask the user which of the files in this directory should be moved to offline tape storage.

  4. Here's one way to do it:

        unlink @ARGV;
    

    or, if you want to warn the user of any problems:

        foreach (@ARGV) {
          unlink $_ or warn "Can't unlink '$_': $!, continuing...\n";
        }
    

    Here, each item from the command-invocation line is placed individually into $_, which is used as the argument to unlink. If something goes wrong, the warning gives the clue why.

  5. Here's one way to do it:

        use File::Basename;
        use File::Spec;
        my($source, $dest) = @ARGV;
        if (-d $dest) {
          my $basename = basename $source;
          $dest = File::Spec->catfile($dest, $basename);
        }
        rename $source, $dest
          or die "Can't rename '$source' to '$dest': $!\n";
    

    The workhorse in this program is the last statement, but the remainder of the program is necessary when we are renaming into a directory. First, after declaring the modules we're using, we name the command-line arguments sensibly. If $dest is a directory, we need to extract the basename from the $source name and append it to the directory ($dest). Once $dest is patched up, if needed, the rename does the deed.

  6. Here's one way to do it:

        use File::Basename;
        use File::Spec;
        my($source, $dest) = @ARGV;
        if (-d $dest) {
          my $basename = basename $source;
          $dest = File::Spec->catfile($dest, $basename);
        }
        link $source, $dest
          or die "Can't link '$source' to '$dest': $!\n";
    

    As the hint in the exercise description said, this program is much like the previous one. The difference is that we'll link rather than rename. If your system doesn't support hard links, you might have written this as the last statement:

        print "Would link '$source' to '$dest'.\n";
    

  7. Here's one way to do it:

        use File::Basename;
        use File::Spec;
        my $symlink = $ARGV[0] eq '-s';
        shift @ARGV if $symlink;
        my($source, $dest) = @ARGV;
        if (-d $dest) {
          my $basename = basename $source;
          $dest = File::Spec->catfile($dest, $basename);
        }
        if ($symlink) {
          symlink $source, $dest
            or die "Can't make soft link from '$source' to '$dest': $!\n";
        } else {
          link $source, $dest
            or die "Can't make hard link from '$source' to '$dest': $!\n";
        }
    

    The first few lines of code (after the two use declarations) look at the first command-line argument, and if it's -s, we're making a symbolic link, so we note that as a true value for $symlink. If we saw -s, we would need to get rid of it in the next line. The next few lines are cut and pasted from the previous exercise answers. Finally, based on the truth of $symlink, we'll create a symbolic link or a hard link. We updated the dying words to make it clear which kind of link we were attempting.

  8. Here's one way to do it:

        foreach (<.* *>) {
          my $dest = readlink $_;
          print "$_ -> $dest\n" if defined $dest;
        }
    

    Each item resulting from the glob ends up in $_ one by one. If the item is a symbolic link, then readlink returns a defined value, and the location is displayed. If not, then the condition fails, and we can skip over it.

    Previous
    Table of Contents
    Next