DEV Community

Mahiro Hirakawa
Mahiro Hirakawa

Posted on

A double-quoted $HOME sent three of my directories nowhere I could find them

A double-quoted $HOME sent three of my directories nowhere I could find them

I was calling into WSL from a PowerShell script, the way I do a dozen times a day: build a command string, hand it to wsl, let bash do the real work on the Linux side. Something like this, simplified down to the part that mattered:

wsl -d Ubuntu-24.04 -- bash -c "mkdir -p \$HOME/build && cd \$HOME/build && ..."
Enter fullscreen mode Exit fullscreen mode

The backslash in front of $HOME is the reflex you develop once you've been burned by shell interpolation. It says "not yet, wait until you're inside bash." Except PowerShell doesn't treat backslash as an escape character at all. Its escape character is the backtick. So \$HOME inside a double-quoted PowerShell string isn't an escaped variable reference, it's a literal backslash sitting next to a variable reference, and PowerShell expands $HOME right there, before the string ever leaves the PowerShell process.

$HOME is one of PowerShell's automatic variables. On this machine it resolves to C:\Users\mhira. So the string I thought I was building for bash to expand later had already been rewritten by the time it left PowerShell, and the backslashes in that Windows path got eaten somewhere in the trip through quoting and re-quoting on the way into wsl.exe. What arrived on the bash side wasn't $HOME/build, it was something closer to C:Usersmhirabuild, no separators, not an absolute path, not anything bash would treat as "home."

Because it didn't start with /, bash treated it as relative. mkdir -p didn't fail. It just made a directory with that literal, mangled name inside whatever the current working directory happened to be. No error, no warning, a perfectly normal-looking mkdir -p exit code of zero. I found three of these later, sitting in three different working directories, named after variations of a broken Windows path, holding nothing anyone was looking for.

That's the part I want to be honest about: I didn't watch the string die in real time. I reconstructed the mechanism after the fact from the wreckage, and I'm fairly confident in the PowerShell-escaping half of it (backslash isn't an escape char, automatic variables expand inside double quotes, that's documented and reproducible). I'm less certain exactly where the Windows-style backslashes got stripped on the way into the WSL command line versus being consumed by bash's own parsing once they landed. It doesn't change the fix, but I'd rather say "probably lost in transit through wsl.exe's argument handling" than pretend I traced every byte.

The fix is the boring one: single quotes.

wsl -d Ubuntu-24.04 -- bash -c 'mkdir -p $HOME/build && cd $HOME/build && ...'
Enter fullscreen mode Exit fullscreen mode

Single-quoted strings in PowerShell don't interpolate anything, $HOME stays as the literal two characters $ and H, O, M, E all the way to bash, and bash resolves it against its own environment once it's actually running. If the command has to mix PowerShell variables and bash variables in the same call, string-formatting the PowerShell parts in first and leaving everything meant for bash inside single quotes is the only version of this I've stopped getting wrong.

The part that makes this worth writing down instead of just fixing quietly: nothing about the failure looked like a failure. mkdir -p succeeded. The script continued. The only signal was a small pile of oddly-named directories that I noticed later, purely because I happened to be looking at directory listings for something unrelated. If I hadn't been, they'd still be there.

Top comments (0)