ReaddirSync behavior depends on the current working directory (cwd/pwd) if we pass a drive letter with or without a trailing "\".
It's very easy to reproduce. Here, I am on "d:" drive, and I have a "d:\Temp" folder, containing "A" and "B" sub-folders.
D:\Temp> gci | % { $_.Name }
A
B
D:\Temp> node
> var fs = require('fs');
undefined
> fs.readdirSync('d:')
[ 'A', 'B' ]
> fs.readdirSync('d:\\')
[ '$RECYCLE.BIN',
'Cache',
'DEV',
'Installers',
'log',
'pagefile.sys',
'System Volume Information',
'Temp',
'VM' ]
> .exit
D:\Temp>
It's a very suprising behavior, and more importantly, it's not documented if expected.
Can reproduce on Win 7 x64 with Node.js 7 with additional observation: fs.readdirSync('d:') != fs.readdirSync('d:\\') only if d: is a current disk and cwd != d:\.
However, if d: is a current disk, fs.readdirSync('c:') still = fs.readdirSync('c:\\').
Yep, forgot to mention that it's only when doing readdirSync on the "current" disk (the one on which node has been launched).
I haven't tested, but I assume the problem also exists with readdir.

I haven't tested, but I assume the problem also exists with
readdir.
Yep.

It seems that dir command has the same quirk: if the drive letter is not followed by a path (\ at least), the cwd content is displayed.
J:\temp>dir . /b
-arch
-mus
-v
Lv
J:\temp>dir j: /b
-arch
-mus
-v
Lv
J:\temp>dir j:\ /b
DAT
temp
I can't find this in the dir documentation, maybe this is documented in some other place.
Oh, it makes sense then. The dir and cd commands behave weirdly with multiple drives (like the cd on another drive that doesn't change the drive letter).
I would say that it's still a surprising behavior, and thus should be fixed.
So, I’m not a frequent Windows user, but from the little experience I have with drive-specific working dirs, it seems like Node is behaving as expected? Maybe this should just be mentioned in the docs?
I am not sure users expect from Node fs functions the same quirks as from the shell with the old DOS roots. Maybe I am wrong.
If I’m reading this issue correctly, there’s nothing shell-specific about this, which is kind of why Node is having this behaviour? I mean, yes, drive-specific wds are not very intuitive when first encountering them, but at least they are consistent…
Also, two questions:
fs.readdir('d:'), how would they replicate that when d: is translated to d:\?It seems you are right. So maybe this just could be added to fs documentation. However, it is unclear for me how wide the impact is and how many fs methods could be affected.
I agree with @vsemozhetbyt .
The current behavior may be consistent with how batch work, but I'm pretty sure most developers these days are not familiar with the horrors that lurk in batch scripts.
Keeping a weirdness that will trip people up just to be consistent with a 30 years old tech seems... wrong to me.
I would also argue, more importantly, that it's dependent on the drive you were on when starting node. So the behavior will not be the same for everyone.
For instance, if I distribute my app through NW.js (which I will) or Electron, depending on the drive the app is installed on, it may or may not work.
You might also have multiple servers with multiple configurations (it's not a good idea, but we're talking bad-ideas-that-should-work here), and your web site will work on one and not the other.
I believe it is not specific to particular command, but windows built-in behavior since ever.
if you just type d: in command prompt its change the drive and keep last cwd in that drive.
You can copy files across drives without specifying path, if you know you were in that folder.
This is surprising Windows behavior, not Node behavior. What is surprising is that drive letters are not exactly like directory paths, and cannot be expected to work exactly the same. Drives (like C:) are very Windows specific, so they should have Windows behavior. I do not see a problem of consistency because directories (like C:\) behave consistently.
Node is correct as it is: https://github.com/nodejs/node/blob/3c1e5b366f/lib/path.js#L170-L175 . Highlighting this in the docs would be great, I believe this is often unexpected for people without Windows background.
I insist on the fact that the behavior depends on the drive the user was when running node.
Running the set of fs commands after
C:\temp> node
yields different results than after
D:\temp> node
Same if I have an electron/nw.js app that users installed on different drives, or if I set a different cwd on my shortcut to this app : my users will have different behaviors with the same codebase.
That Windows is broken in the same way seems of little importance to me. Replicating the Windows behavior will break things for many people, and I'm pretty sure (almost) nobody relies on it. Of course, I may be wrong, but I doubt it.
I believe this is often unexpected for people without Windows background.
I've been working on Windows for more than 10 years and have never encountered this (I knew about cd weirdness but not dir).
Plus, most Windows coders don't ever use the console.
I just found out that Unix also has weirdnesses when using ls without trailing slash, so maybe it's much more widely known that I think.
Here using oh-my-zsh on MacOS Sierra:
> ls -l /tmp
lrwxr-xr-x@ 1 root wheel 11 10 oct 15:13 /tmp -> private/tmp
> ls -l /tmp/
total 8
drwxr-xr-x@ 6 tbroust wheel 204 4 nov 09:37 VSCode Crashes
drwx------ 3 tbroust wheel 102 4 nov 09:32 com.apple.launchd.0LqMMQjDgk
drwx------ 3 tbroust wheel 102 4 nov 09:32 com.apple.launchd.0vXKPX7vRQ
With this additional information, I agree that just adding a warning in the doc is more pertinent.
@cosmo0 I think that doesn’t affect readdir, though. (Also, I think the ls behaviour makes sense here, but that’s obviously subjective, too. :smile:)
> ls -l /tmp
lrwxr-xr-x@ 1 root wheel 11 10 oct 15:13 /tmp -> private/tmp
Link itself is a file, so that is the correct behavior, imo.
@cosmo0 Can you show what you get when you run node from different drives? Before running node, make sure the cwd is set for both, by changing to the drive and cd to the directory. Then run env and look for the !C:= and !D:= lines on the top.
Running readdirSync('c:') then readdirSync('d:') without trailing slash from c:\temp then d:\temp :
I am using Powershell, not cmd. More on that below.
From c:\temp, we get the current directory of c: then the root of d:
C:\temp> node
> require('fs').readdirSync('c:')
[ X', 'Y' ]
> require('fs').readdirSync('d:')
[ '$RECYCLE.BIN',
'DEV',
'pagefile.sys',
'System Volume Information',
'Temp',
'VM' ]
then from d:\temp we get the root of c: and the current directory of d:
D:\temp> node
> require('fs').readdirSync('d:')
[ 'A', 'B' ]
> require('fs').readdirSync('c:')
[ '$Recycle.Bin',
'BOOTNXT',
'Config.Msi',
'Documents and Settings',
'hiberfil.sys',
'MSOCache',
'PerfLogs',
'Program Files',
'Program Files (x86)',
'ProgramData',
'swapfile.sys',
'System Volume Information',
'temp',
'Users',
'Windows' ]
As you can see, depending on where you run node from, the result of the same command is different.
Your request of env result has led me to realize that it also depends on which shell I use. The result above is from Powershell. cmd "remembers" different cwd for each drives, but Powershell doesn't.
In Powershell:
D:\temp> env
!::=::\
ALLUSERSPROFILE=C:\ProgramData
...
D:\temp> cd c:\temp
C:\temp> env
!::=::\
ALLUSERSPROFILE=C:\ProgramData
...
C:\temp>
Which leads to yet another behavior when using cmd to run node:
[d:\Temp]
> node
> require('fs').readdirSync('c:')
[ '$Recycle.Bin',
'BOOTNXT',
'Config.Msi',
'Documents and Settings',
'hiberfil.sys',
'MSOCache',
'PerfLogs',
'Program Files',
'Program Files (x86)',
'ProgramData',
'swapfile.sys',
'System Volume Information',
'temp',
'Users',
'Windows' ]
> require('fs').readdirSync('d:')
[ 'A', 'B' ]
> .exit
[d:\Temp]
> cd /d c:\temp
[c:\temp]
> env
!::=::\
!C:=c:\temp
!D:=d:\Temp
...
[c:\temp]
> node
> require('fs').readdirSync('c:')
[ 'X', 'Y' ]
> require('fs').readdirSync('d:')
[ 'A', 'B' ]
> .exit
The cmd behavior is correct. PowerShell is indeed not working as expected. I'll try to fix this but it may take me a while, so if someone else wants to give it a try, please do.
Note: good first contribution refers to the doc clarification above, fixing this might be harder.
I added the confirmed-bug label just now, not @cosmo0 (GitHub bug?).
Apparently I both added and deleted the label 😄
According to this link changing location in PowerShell doesn't actually change the cwd for non-PowerShell commands.
Has this issue been resolved?
Has this issue been resolved?
Don't think so, the behaviour still needs documenting.
@gibfahn But in my computer, readdirSync("c:") under D: seems return my home dir but not root of C now.
Eg. If my username is xadillax, readdirSync("c:") returns the list of c:\Users\XadillaX\.
Yes, that's the expected behaviour. When you use C: without the \ from a different drive, you get the current working directory of C:, not the root.
C: :: cd to the current working directory in C:, in your case c:\Users\XadillaX\
cd Desktop :: Changes the C: cwd to c:\Users\XadillaX\Desktop
D: :: Changes to the current working directory in D:
dir C: :: Shows the contents of the cwd in C: , now c:\Users\XadillaX\Desktop
dir C:\ :: Shows the contents of the root of C
It should be documented, but it's a Windows oddity, not a node one. You just have to remember that C:\ means "the root of drive C", and C: means "the last directory you were in in drive C".
D:\> C:
C:\Users\gib> cd Desktop
C:\Users\gib\Desktop> D:
D:\> dir C:
Volume in drive C has no label.
Volume Serial Number is E0D4-8E7B
Directory of C:\Users\gib\Desktop
04/05/2017 11:46 AM <DIR> .
04/05/2017 11:46 AM <DIR> ..
0 File(s) 0 bytes
2 Dir(s) 71,552,552,960 bytes free
D:\> dir C:\
Directory of C:\
04/12/2017 10:12 AM <DIR> Users
05/03/2017 10:32 AM <DIR> Windows
2 File(s) 4,112 bytes
12 Dir(s) 71,552,552,960 bytes free
I've created a PR for doc update here: https://github.com/nodejs/node/pull/13330
I think that node behaves here correctly. It gives the same results (on both cmd and powershell) as this simple prog:
#include <windows.h>
#include <iostream>
int main() {
int main() {
TCHAR buffer[1024];
GetFullPathName("C:", 1024, buffer, nullptr);
std::cout << "c: " << buffer << "\n";
GetFullPathName("D:", 1024, buffer, nullptr);
std::cout << "d: " << buffer << "\n";
}