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

Good day Perl monks

I was wondering if it was possible to test if a variable was a folder handle. I could not find any documentation or forum posts so far. I have written some code which works as shown below. I was wondering if there was a better way to perform the folder testing section as my code seems inelegant.

Also, I am aware that the file testing section could be improved but there is already plenty of comment on that in the following posts

http://www.perlmonks.org/?node_id=980665 Best way to check if something is a file handle?

https://stackoverflow.com/questions/3214647/what-is-the-best-way-to-determine-if-a-scalar-holds-a-filehandle

https://stackoverflow.com/questions/3807231/how-can-i-test-if-i-can-write-to-a-filehandle/4200474#4200474

#!perl use strict; use warnings; use Data::Dumper; use File::Temp qw(tempfile tempdir); use Scalar::Util qw(looks_like_number openhandle); my $fh = tempfile(); #my $dh = tempdir(); opendir(my $dh, '.') || die "Can't opendir: $!"; my $string = 'path\to\file\name'; print Dumper handleRef($fh); print Dumper handleRef($dh); print Dumper handleRef($string); sub handleRef { my ($handle) = @_; my ($fh, $dataTypeHash) = @_; # Folder test (may be possible to improve)... my $folderType = eval { no warnings; telldir ($fh); }; if (looks_like_number($folderType)){ $folderType = 1; } else { $folderType = 0; } return 'FOLDERHANDLE' if ($folderType); # File test... my $handleType = openhandle($handle); return 'FILEHANDLE' if (openhandle($handle)); # Everything else... return ref \$fh; }

Replies are listed 'Best First'.
Re: Testing for a directory handle
by AnomalousMonk (Archbishop) on Jan 29, 2018 at 03:51 UTC

    Maybe I'm missing something, but wouldn't  -d (see -X in perlfunc) do the trick (if a "folder" is a directory)? (If a folder is a file, then maybe  -f instead?)


    Give a man a fish:  <%-{-{-{-<

      Unfortunately using -f and -d doesn't work if someone has already opened them as handles, instead it just throws an error saying 'The dirfd function is unimplemented'

        The dirfd function is unimplemented

        This only happens (for me) if the handle is a dirhandle:
        C:\_32>perl -le "opendir $rd, 'special' or die $!; print 'dir' if -d $ +rd; print 'file' if -f $rd;" The dirfd function is unimplemented at -e line 1. C:\_32>perl -le "opendir RD, 'special' or die $!; print 'dir' if -d RD +; print 'file' if -f RD;" The dirfd function is unimplemented at -e line 1. C:\_32>perl -le "opendir RD, 'special' or die $!; print 'dir' if -d \* +RD; print 'file' if -f \*RD;" The dirfd function is unimplemented at -e line 1.

        With a filehandle it seems fine:
        C:\_32>perl -le "open $rd, 'try.pl' or die $!; print 'dir' if -d $rd; +print 'file' if -f $rd;" file C:\_32>perl -le "open RD, 'try.pl' or die $!; print 'dir' if -d RD; pr +int 'file' if -f RD;" file C:\_32>perl -le "open RD, 'try.pl' or die $!; print 'dir' if -d \*RD; +print 'file' if -f \*RD;" file
        Surely this behaviour with dirhandles is a serious bug that should be reported to p5p ?
        I can see nothing in the -X documentation that allows such behaviour, and I can see plenty there that disallows it.

        Cheers,
        Rob
Re: Testing for a directory handle
by salva (Canon) on Jan 29, 2018 at 09:40 UTC
    I was going to point you into B::IO direction, but it seems that information is not exposed there either.

    You could write a little XS module to look into the xpvio structure. I think the dirp slot is the one telling you it is an open directory handle.

Re: Testing for a directory handle
by kcott (Archbishop) on Jan 29, 2018 at 21:47 UTC

    G'day ns550,

    If you attempt to use telldir on an invalid directory handle, you'll get a "telldir() attempted on invalid dirhandle" warning (see perldiag). You can promote that warning to an error with FATAL (see the warnings pragma). That error can now be captured and tested.

    I created some test files:

    $ ls -al pm_1208018_test_dir* pm_1208018_test_dir: total 0 drwxr-xr-x 3 ken staff 102 Jan 30 07:08 . drwxr-xr-x 20 ken staff 680 Jan 30 07:43 .. -rw-r--r-- 1 ken staff 0 Jan 30 07:08 pm_1208018_test_file pm_1208018_test_dir_closed: total 0 drwxr-xr-x 2 ken staff 68 Jan 30 07:40 . drwxr-xr-x 20 ken staff 680 Jan 30 07:43 .. pm_1208018_test_dir_empty: total 0 drwxr-xr-x 2 ken staff 68 Jan 30 07:29 . drwxr-xr-x 20 ken staff 680 Jan 30 07:43 ..

    I used a standard alias of mine:

    $ alias perle alias perle='perl -Mstrict -Mwarnings -Mautodie=:all -E'

    I started with a simple, one-liner test. I then added more and more tests: the result wrapped around four lines. Here it is; expanded for ease of viewing. I've also annotated the output in situ.

    $ perle ' open my $fh, "<", "pm_1208018_test_dir/pm_1208018_test_file"; opendir(my $dh, "pm_1208018_test_dir"); opendir(my $eh, "pm_1208018_test_dir_empty"); opendir(my $ch, "pm_1208018_test_dir_closed"); closedir $ch; my $nh = "42"; my $uh; my %h = (fh => $fh, dh => $dh, nh => $nh, eh => $eh, uh => $uh, ch + => $ch); for (keys %h) { use warnings FATAL => "io"; eval { my $x = telldir $h{$_}; say "$_ is a dirhandle"; 1 } or say "$_ is not a dirhandle" } ' fh is not a dirhandle # fh = filehandle nh is not a dirhandle # nh = not a handle (just the number 42) eh is a dirhandle # eh = dirhandle to "pm_1208018_test_dir_empty +" dh is a dirhandle # dh = dirhandle to "pm_1208018_test_dir" ch is not a dirhandle # ch = closed handle uh is not a dirhandle # uh = undefined handle (declared but uninitia +lised)

    [Note that "pm_1208018_test_dir_empty" isn't technically empty. On *nix machines it shows the "." and ".." entries. I'm not sure how that works on MSWin. I've left it in as a potential edge case.]

    — Ken

      If you attempt to use telldir on an invalid directory handle, you'll get a "telldir() attempted on invalid dirhandle" warning

      Also, telldir returns undef in such a case, so I'm thinking we could alternatively just test for definedness:
      C:\_32>perl -le "open D, '<','test.txt' or die $!; print 'dir' if defi +ned telldir D; print 'DONE';" DONE C:\_32>perl -le "opendir D, 'comp' or die $!; print 'dir' if defined t +elldir D; print 'DONE';" dir DONE
      Of course, if it turns out that telldir was given something other than a handle, then it's a fatal error:
      C:\_32>perl -le "open D, '<','test.txt' or die $!; print 'dir' if defi +ned telldir $x; print 'DONE';" Bad symbol for dirhandle at -e line 1.
      So that scenario would need to be avoided.

      On *nix machines it shows the "." and ".." entries

      Yes, it's the same on Windows.

      Cheers,
      Rob
        "Yes, it's the same on Windows."

        Thanks for that. I had a feeling it might have been the same but wasn't sure. It's been many years since I've written any code to interact with a MSWin filesystem.

        — Ken

      On *nix machines it shows the "." and ".." entries. I'm not sure how that works on MSWin. I've left it in as a potential edge case.

      Well, as usual for Microsoft software: Don't expect consistent behaviour. Most directories have the . and .. entries. For the root directory of any drive, it heavily depends on the drive type. Some root directories have the . and .. entries, others don't. See Re^6: readdir() on a sysopen() handle? for details.

      Alexander

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

        Thanks for the additional information regarding this. It doesn't affect me personally (either at work or home) at the moment, and probably won't do in the forseeable future; however, for those working with the MSWin filesystem, this could be important.

        I suppose the real issue is how this might affect telldir in the context of the code I posted. I'm not in a position to test this but I'll attempt to paraphrase in *nix parlance. Given a truly empty directory (root or otherwise), e.g.

        $ ls -al fred ls: cannot access 'fred': No such file or directory $ mkdir fred $

        Where a listing showed

        $ ls -al fred total 0 $

        instead of

        $ ls -al fred total 0 drwxr-xr-x 2 ken staff 68 Jan 31 12:57 . drwxr-xr-x 18 ken staff 612 Jan 31 12:57 .. $

        How would the "eh" handle be reported? Just to be clear, that's the one I showed as:

        eh is a dirhandle       # eh = dirhandle to "pm_1208018_test_dir_empty"

        If someone could test that and report their findings, it could provide useful information to anyone wanting to use this technique in the future.

        — Ken

      Cool, good thinking

      I will keep these work-arounds in mind when I am coding

      Thanks, Matt

        If you do find this general technique useful, I'll just highlight two sections from the warnings pragma documentation which you may want to look at:

        Category Hierarchy
        The "io" category has seven sub-categories. Depending on what you're actually doing, you may find these useful for more specific tests.
        Fatal Warnings
        This discusses various caveats with making warnings fatal. It also shows how turn them back to their original non-fatal state.

        — Ken