http://www.perlmonks.org?node_id=979494

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

On UNIX/Linux/Mac, this:
$file = '""'; if (-e $file) { print "Yep\n"; } else {print "Nope\n"; }
prints 'Nope'.
On Windoze it prints 'Yep.'

What am I missing. There is not path/file of '""' on the system, neither now no ever.

Slightly annoying that it appears I'm going to have to sniff the path/file string to see if it is an empty pair of double quotes. (I don't control what is feeding me empty double quotes as a path/filename string.)

I'm not a Windows Guru. On windoze, is the empty double quote string used for some special purpose? Why would the Perl -e test think it is a valid path/file that actually exists on the system? Thanks

Replies are listed 'Best First'.
Re: true from (-e "") on Windoze (" is an illegal filename character
by BrowserUk (Patriarch) on Jul 02, 2012 at 19:57 UTC

    The quote " character is illegal in Windows filenames. Therefore, what you are passing to the OS is taken as the empty string.

    And when you search for the empty string, dir "", you get everything.

    You are effectively asking, is there anything in the current directory. Which will always be true.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

      The file name is illegal, but no, we won't return an error! yuck.

      Is there a system call that can be called instead of C's stat?

        The file name is illegal, but no, we won't return an error! yuck.

        Not sure why "yuck"?

        Either, it should be treated as illegal and return "The system cannot find the file specified",

        or it is a valid alias for CWD and should do what it now does.

        I can't see what other option there is?

        Is there a system call that can be called instead of C's stat?

        I'm not sure what that would achieve?

        Most of the values returned by stat that actually have any meaning on NTFS, are available via GetFileAttributesEx().

        But be aware, Win32.c:Win32_stat() already calls this (but only if _stat()/_stat64() fail??), along with several others (GetFileAttributesA()/GetVolumeInformationA()/GetFileInformationByHandle()) in order to fix-up what they see as bugs.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The start of some sanity?

      The quote " character is illegal in Windows filenames

      Therefore the OP should get the error:
      The filename, directory name, or volume label syntax is incorrect.
      That's the same error as produced by dir "\"\""

      For mine, it's a bug that perl behaves the way it does - and I think the OP should submit a bug report by running perlbug and following the prompts.

      Cheers,
      Rob

        As I showed in Re^3: true from (-e "") on Windoze (" is an illegal filename character, it is the MS CRT stat (and I believe the Winapi function(s) that underlies it), that chooses to treat a filename passed as '""' as meaning 'the current directory'.

        As such, I don't think that it can be blamed on Perl.

        That's not to say that Perl might not choose to prevent it by detecting it and failing, before passing it through to stat, and so regularise the cross-platform Perl behaviour.

        But, it would be 'correcting' for a bug -- if you wish to call it that -- in the CRT, not Perl per se.

        I also suspect that the 'bug' probably goes all the way back to MSDOS, where it was considered a useful behaviour.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The start of some sanity?

        $ dir "\"\"" The network path was not found. $ echo %ERRORLEVEL% 1
      And when you search for the empty string, dir "", you get everything.

      dir is not stat().

      The quote " character is illegal in Windows filenames. Therefore, what you are passing to the OS is taken as the empty string.

      No. If it would, stat() in the C example would return 0 (success), not -1 (error).

      You are effectively asking, is there anything in the current directory. Which will always be true.

      Wrong conclusion.

      Alexander

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

        Sorry, but you are wrong. Here is the proof:

        #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> int main( int argc, char **argv ) { struct _stat buf1, buf2; char *file = "\"\""; printf( "for %s; stat returned: %d\n", file, _stat( file, &buf1 ) +); printf( "gid: %d\natime:%I64x\nctime:%I64x\ndrive:%d\n" "inode:%d\nmode:%x\nmtime:%I64x\nnlink:%d\nrdev:%d\n" "size:%d\nuid:%d\n", buf1.st_gid, buf1.st_atime, buf1.st_ctime, buf1.st_dev, buf1.st_ino, buf1.st_mode, buf1.st_mtime, buf1.st_nlink, buf1. +st_rdev, buf1.st_size, buf1.st_uid ); printf( "\nFor %s; stat returned: %d\n", argv[1], _stat( argv[1], +&buf2 ) ); printf( "gid: %d\natime:%I64x\nctime:%I64x\ndrive:%d\n" "inode:%d\nmode:%x\nmtime:%I64x\nnlink:%d\nrdev:%d\n" "size:%d\nuid:%d\n", buf2.st_gid, buf2.st_atime, buf2.st_ctime, buf2.st_dev, buf2.st_ino, buf2.st_mode, buf2.st_mtime, buf2.st_nlink, buf2. +st_rdev, buf2.st_size, buf2.st_uid ); return 1; }

        That code first stats the filename '""'; then it stats the pathname passed from the command line in argv1.

        Run passing the path of the current directory, both sets of stats are identical:

        C:\test>stat c:\test for ""; stat returned: 0 gid: 0 atime:4ff203c6 ctime:49bba448 drive:2 inode:0 mode:41ff mtime:4ff203c6 nlink:1 rdev:2 size:0 uid:0 For c:\test; stat returned: 0 gid: 0 atime:4ff203c6 ctime:49bba448 drive:2 inode:0 mode:41ff mtime:4ff203c6 nlink:1 rdev:2 size:0 uid:0

        Explain that in any way other than '""' is being taken to mean 'the current directory'?


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The start of some sanity?

Re: true from (-e "") on Windoze
by Jenda (Abbot) on Jul 02, 2012 at 19:56 UTC

    It's not just two doublequotes. Just one doublequote, three doublequotes, ... seems any number of doublequotes work. Considering the fact that a doublequote is an illegal character in file names in Windows ...

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

      It's not just two doublequotes. Just one doublequote, three doublequotes, ... seems any number of doublequotes work.

      ... but not in C code, only in perl.

      Considering the fact that a doublequote is an illegal character in file names in Windows ...

      ... there can be no file or directory with such a name, so stat() should fail, either with "file not found" or with "invalid file name". But it should not stat() the current directory instead.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: true from (-e "") on Windoze
by afoken (Chancellor) on Jul 02, 2012 at 19:47 UTC

    Looks like a bug to me.

    Linux:

    >uname -a Linux enterprise 2.6.37.6-foken #2 SMP Sun Aug 14 02:20:59 CEST 2011 x +86_64 AMD Athlon(tm) II Neo N36L Dual-Core Processor AuthenticAMD GNU +/Linux >perl -E '$file=q[""]; say (-e($file) ? "yes" : "no")' no >perl -V Summary of my perl5 (revision 5 version 12 subversion 3) configuration +: Platform: osname=linux, osvers=2.6.35.10, archname=x86_64-linux-thread-multi uname='linux midas64 2.6.35.10 #2 smp thu jan 6 19:06:19 cst 2011 +x86_64 amd athlon(tm) ii x2 235e processor authenticamd gnulinux ' config_args='-de -Dprefix=/usr -Dvendorprefix=/usr -Dcccdlflags=-f +PIC -Dinstallprefix=/usr -Dlibpth=/usr/local/lib64 /usr/lib64 /lib64 +-Doptimize=-O2 -fPIC -Dusethreads -Duseithreads -Dpager=/usr/bin/less + -isr -Dinc_version_list=5.12.2 5.12.1 5.12.0 5.10.1 5.10.0 5.8.8 5.8 +.7 5.8.6 5.8.5 5.8.4 5.8.3 5.8.2 5.8.1 5.8.0 -Darchname=x86_64-linux' hint=recommended, useposix=true, d_sigaction=define useithreads=define, usemultiplicity=define useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=und +ef use64bitint=define, use64bitall=define, uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing + -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_ +FILE_OFFSET_BITS=64', optimize='-O2 -fPIC', cppflags='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -f +stack-protector -I/usr/local/include' ccversion='', gccversion='4.5.2', gccosandvers='' intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=1 +6 ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', + lseeksize=8 alignbytes=8, prototype=define Linker and Libraries: ld='cc', ldflags =' -fstack-protector' libpth=/usr/local/lib64 /usr/lib64 /lib64 libs=-lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc perllibs=-lnsl -ldl -lm -lcrypt -lutil -lpthread -lc libc=/lib64/libc-2.12.2.so, so=so, useshrplib=false, libperl=libpe +rl.a gnulibc_version='2.12.2' Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E' cccdlflags='-fPIC', lddlflags='-shared -O2 -fPIC -fstack-protector +' Characteristics of this binary (from libperl): Compile-time options: MULTIPLICITY PERL_DONT_CREATE_GVSV PERL_IMPLICIT_CONTEXT PERL_MALLOC_WRAP USE_64_ +BIT_ALL USE_64_BIT_INT USE_ITHREADS USE_LARGE_FILES USE_PERLIO USE_PERL_ATOF USE_REENTRANT_API Built under linux Compiled at Jan 26 2011 12:39:46 %ENV: PERL_UNICODE="SDL" @INC: /usr/lib64/perl5/site_perl/5.12.3/x86_64-linux-thread-multi /usr/lib64/perl5/site_perl/5.12.3 /usr/lib64/perl5/vendor_perl/5.12.3/x86_64-linux-thread-multi /usr/lib64/perl5/vendor_perl/5.12.3 /usr/lib64/perl5/5.12.3/x86_64-linux-thread-multi /usr/lib64/perl5/5.12.3 /usr/lib64/perl5/site_perl /usr/lib64/perl5/vendor_perl .

    Windows:

    C:\Users\alex>perl -E "$file=chr(34).chr(34); say $file.' '.(-e($file) + ? 'yes' : 'no')" "" yes C:\Users\alex>ver Microsoft Windows [Version 6.1.7601] C:\Users\alex>dir Volume in drive C is System Volume Serial Number is B663-2E5B Directory of C:\Users\alex 24.06.2012 19:12 <DIR> . 24.06.2012 19:12 <DIR> .. 25.05.2012 23:46 <DIR> .gegl-0.0 24.06.2012 19:21 <DIR> .gimp-2.6 24.06.2012 19:12 6.262 .recently-used.xbel 28.05.2012 14:19 <DIR> .thumbnails 06.05.2012 23:44 <DIR> Contacts 24.06.2012 19:12 <DIR> Desktop 24.06.2012 19:41 <DIR> Documents 01.07.2012 19:22 <DIR> Downloads 24.06.2012 19:32 <DIR> Favorites 06.05.2012 23:44 <DIR> Links 16.06.2012 12:54 <DIR> Music 25.05.2012 23:50 <DIR> Pictures 06.05.2012 23:44 <DIR> Saved Games 06.05.2012 23:44 <DIR> Searches 06.05.2012 23:44 <DIR> Videos 15.06.2012 21:05 <DIR> Virtual Machines 1 File(s) 6.262 bytes 17 Dir(s) 11.921.534.976 bytes free C:\Users\alex>perl -V Summary of my perl5 (revision 5 version 14 subversion 2) configuration +: Platform: osname=MSWin32, osvers=4.0, archname=MSWin32-x64-multi-thread uname='Win32 strawberryperl 5.14.2.1 #1 Tue Nov 22 22:40:59 2011 x +64' config_args='undef' hint=recommended, useposix=true, d_sigaction=undef useithreads=define, usemultiplicity=define useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=und +ef use64bitint=define, use64bitall=undef, uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='gcc', ccflags =' -s -O2 -DWIN32 -DWIN64 -DCONSERVATIVE -DPERL +_TEXTMODE_SCRIPTS -DUSE_SITECUSTOMIZE -DPERL_IMPLICIT_CONTEXT -DPERL_ +IMPLICIT_SYS -fno-strict-aliasing -mms-bitfields', optimize='-s -O2', cppflags='-DWIN32' ccversion='', gccversion='4.4.7', gccosandvers='' intsize=4, longsize=4, ptrsize=8, doublesize=8, byteorder=12345678 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=1 +2 ivtype='long long', ivsize=8, nvtype='double', nvsize=8, Off_t='lo +ng long', lseeksize=8 alignbytes=8, prototype=define Linker and Libraries: ld='g++', ldflags ='-s -L"C:\strawberry\perl\lib\CORE" -L"C:\straw +berry\c\lib"' libpth=C:\strawberry\c\lib C:\strawberry\c\x86_64-w64-mingw32\lib libs=-lmoldname -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 +-ladvapi32 -lshell32 -lole32 -loleaut32 -lnetapi32 -luuid -lws2_32 -l +mpr -lwinmm -lversion -lodbc32 -lodbccp32 -lcomctl32 perllibs=-lmoldname -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdl +g32 -ladvapi32 -lshell32 -lole32 -loleaut32 -lnetapi32 -luuid -lws2_3 +2 -lmpr -lwinmm -lversion -lodbc32 -lodbccp32 -lcomctl32 libc=, so=dll, useshrplib=true, libperl=libperl514.a gnulibc_version='' Dynamic Linking: dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' ' cccdlflags=' ', lddlflags='-mdll -s -L"C:\strawberry\perl\lib\CORE +" -L"C:\strawberry\c\lib"' Characteristics of this binary (from libperl): Compile-time options: MULTIPLICITY PERL_DONT_CREATE_GVSV PERL_IMPLICIT_CONTEXT PERL_IMPLICIT_SYS PERL_MALLOC_WRAP PERL_PRESERVE_IVUV PL_OP_SLAB +_ALLOC USE_64_BIT_INT USE_ITHREADS USE_LARGE_FILES USE_PERLIO USE_PERL_ATOF USE_SITECUSTOMIZE Built under MSWin32 Compiled at Nov 22 2011 22:53:05 %ENV: PERL_JSON_BACKEND="JSON::XS" PERL_YAML_BACKEND="YAML" @INC: C:/strawberry/perl/site/lib C:/strawberry/perl/vendor/lib C:/strawberry/perl/lib . C:\Users\alex>

    Even shorter:

    C:\Users\alex>perl -E "stat(chr(34).chr(34)) or die $!; say 'huh?'" huh? C:\Users\alex>

    vs.

    >perl -E 'stat(q[""]) or die $!; say "huh?"' No such file or directory at -e line 1. >

    If stat is successful, what is in stat()s return value?

    C:\Users\alex>perl -MData::Dumper -E "@a=stat(chr(34).chr(34)) or die +$!; say Du mper(\@a)" $VAR1 = [ 2, # device 0, # inode 16895, # mode = 0x41FF = 040777 1, # nlink 0, # uid 0, # gid 2, # rdev 0, # size 1340557931, # atime 1340557931, # mtime 1336337436, # ctime '', # blocksize '' # blocks ]; C:\Users\alex>

    Mode 0777 happens on Windows only for executables (by extension) and for directories. The bit 040000 looks like the directory flag. This fits the picture, because there is no executable extension in the file name.

    Maybe some routine burried deep inside Perl, MinGW or Windows unquotes filenames? Let's look at the stat() output for "." and "..":

    C:\Users\alex>perl -MData::Dumper -E "for $x (chr(34).chr(34),'.','..' +) { @a=stat($x); say Dumper(\@a) }" $VAR1 = [ 2, 0, 16895, 1, 0, 0, 2, 0, 1340557931, 1340557931, 1336337436, '', '' ]; $VAR1 = [ 2, 0, 16895, 1, 0, 0, 2, 0, 1340557931, 1340557931, 1336337436, '', '' ]; $VAR1 = [ 2, 0, 16749, 1, 0, 0, 2, 0, 1339842184, 1339842184, 1247541608, '', '' ]; C:\Users\alex>

    Exact match for ".". Are quotes removed automatically?

    C:\Users\alex>perl -MData::Dumper -E "@a=stat(chr(34).'Desktop'.chr(34 +)); say Dumper(\@a)" $VAR1 = []; C:\Users\alex>perl -MData::Dumper -E "@a=stat(''); say Dumper(\@a)" $VAR1 = []; C:\Users\alex>perl -MData::Dumper -E "@a=stat(chr(34).'..'.chr(34)); s +ay Dumper(\@a)" $VAR1 = []; C:\Users\alex>perl -MData::Dumper -E "@a=stat(chr(34).'.'.chr(34)); sa +y Dumper(\@a)" $VAR1 = []; C:\Users\alex>

    No. I have no clue why '""' is treated like '.' on Windows.

    What happens when I call stat() from C/MinGW?

    #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int main(int argc, char ** argv) { struct stat st; printf("stat(\"\") = %i\n",stat("\"\"",&st)); return 0; }
    X:\>gcc -o stattest.exe stattest.c X:\>stattest stat("") = -1 X:\>

    So, it's very likely neither Windows nor MinGW, so it must be Perl.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: true from (-e "") on Windoze
by frozenwithjoy (Priest) on Jul 02, 2012 at 19:01 UTC
    Does it work as expected if you type in some random letters between the quotes? I don't use Windows, but one way you could get around this is something like:
    if ( $file and -e $file ) { say "yep"; } else { say "nope"; }
      That won't work. Empty double quotes in a string are considered "true" by Perl.

      CountZero

      A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      My blog: Imperial Deltronics
        Works fine for me. Also, here is a handy table for this sort of thing: "Truth tests for different values" on True or False? A Quick Reference Guide. defined $file, however, would be true for an empty string.

        edit: Oops, I didn't catch that there were quotes inside quotes...

      if ($file) returns true, also because the variable has content, two double quotes. I think I'm going to have to just check to see if my $file variable contains a pair of double quotes and only a pair of double quotes. 'Seem like a hack, though. 'Seems like the '-e' test operator would return false on this case.

      That's what makes me wonder if Windoze actually uses the empty double quotes as some kind of file system object/entity. That would make the '-e' test think the empty-double-quote-thing was real and existed..... Don't know that much about the various file system types and how Perl on Windows handles them.

        Windows uses double quotes to solve a problem caused by file (or directory) names which contain spaces.

        dir "C:\Program Files\" Works as intended

        dir  C:\Program Files\ File not found

        dir "" Displays current directory

        OK, how about this? (I've tested and it is working correctly for $file = '""'; on OS X.)
        for ($file) { say "nope (empty quotes)" when /""/; say "yep" when -e $file; default { say "nope (doesn't exist)"} }
        I think you're misinterpreting comments preceding Re^2: true from (-e "") on Windoze; I think the fact that the VALUE of $file is -- per se -- true is borking your expectations.

        I don't recall any version of windows accepting "" or '""' or """" as a filename or filename element ...or any of the DOS/doze FS using "", et al, for anything.

        Ya, I didn't catch that they were quotes within quotes. Out of curiosity, why/how is that happening?
      Yes, it does. If I put anything between the quotes, I get the expected results. It is just the empty double quotes that unexpectedly returns true from '-e'.
Re: true from (-e "") on Windoze
by monsoon (Pilgrim) on Jul 03, 2012 at 04:33 UTC
    $file = '""'; if (-e $file && !-d $file) { print "Yep\n"; } else {print "Nope\n"; }
    Won't work of course if the file list can contain directories.
Re: true from (-e "") on Windoze
by scorpio17 (Canon) on Jul 03, 2012 at 13:11 UTC
    How about this:
    $file = '""'; if ( -e $file && -f $file ) { print "Yes\n"; } else { print "No\n"; }
      I think -f rules out too much, named pipes for example. It all depends on what kind of files the list can contain.
Re: true from (-e "") on Windoze
by BrowserUk (Patriarch) on Jul 06, 2012 at 14:39 UTC

    FTR: This code shows that the behaviour of treating the filename '""' as an alias for CWD, is an inherent part of the Windows API:

    #include <windows.h> #include <stdio.h> int main( int argc, char **argv ) { char *file = "\"\""; char buf[ 1024 ]; GetFullPathName( file, 1024, buf, NULL ); printf( "%s -> %s\n", file, buf ); return 1; }

    Build and run:

    C:\test>cl /W3 GetFullPathName.c Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64 Copyright (C) Microsoft Corporation. All rights reserved. GetFullPathName.c Microsoft (R) Incremental Linker Version 9.00.21022.08 Copyright (C) Microsoft Corporation. All rights reserved. /out:GetFullPathName.exe GetFullPathName.obj C:\test>GetFullPathName "" -> C:\test\""

    And exposed to Perl directly via:

    C:\test>perl -MWin32 -E"say Win32::GetFullPathName( '\"\"' )" C:\test\""

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?