Linux for Windows developers

Monday, April 15, 2019

A few years back, I started diddling with a Raspberry Pi. Since Raspberry Pis run a flavor of Linux, I had to get familiar with bash again. For me as a Windows Only Developer (WOD), it had been ages since I typed Unix commands, so I had some catching up to do. And learned a ton of new things. And when .NET Core became available for the Pi, and suddenly I could be a Primarily for Windows Developer (PWD).

Then, the Windows Subsystem for Linux (WSL) was added to Windows 10, as a preview. I tried that out and my life became a lot easier. WSL a a 'real' ssh (as opposed to PuTTY for Windows), which made all those tutorials on how to do remote ssh much easier to follow. At first WSL was only available in Developer Mode, but the last few updates of Windows 10 have made it really easy to install Linux distributions on your Windows 10 box, right from the Microsoft Store, and these days I do that on every new development PC I get to work on.

This post is about the things I learned and the things I keep forgetting ;-)

Spoiler: Install the Midnight Commander, tmux and PowerShell Core!

Midnght Commander

Ah, the nostalgia. "Back in the DOS days" the only realistic way to be productive doing file management was the famous Norton Commander. The Midnight Commander (MC) started out as a faithful clone, but is no much more these days. It even supports the mouse. This is not a recommendation: it's a no-brainer. sudo apt-get install mc now.

The problem with Tab

Now using MC on WSL means running it in a terminal window. And that means certain key combinations are handled by the terminal, or simply don't work. So here are a few key combinations I found as alternatives:

General tips:

  • Ctrl+O - reveals that the Midnight Commander is a file manager and a terminal. Ctrl+O hides the panels, so you have a "pure" terminal under your fingers; pressing it again pops the panels back up.

Tab completion and alternatives:

  • ESC Tab - prefix your Tab with ESC and it'll work like regular tab completion
  • Ctrl+X T - copies the file name of the currently selected file to the command line. sibling: Ctrl+X Ctrl+T does the same, but with the file name of the currently selected file in the other pane
  • Ctrl+X P - copies the name of the current directory to the command line. Its sibling Ctrl+X Ctrl+P copies the path of the other pane

Alternative to scrolling to previous commands using Up and Down keys:

  • Ctrl+H - shows the current history of the command line

Copying files to another machine (like a Raspberry Pi):

  • F9, R (for Right), h (for Shell Link). Now enter a connection, prefixed by "sh:", for example: sh://pi@my-raspberry:20000/home/pi. This connects to a machine called "my-raspberry" using user name "pi" over port 20000 and changes directory to "/home/pi" on it. The contents of that directory will be show in the rght pane and you can use the usuals MC ways (F5, F6, F7) to copy and move file hither and dither. see also: SSH.

One of the great improvements in WSL (I don't know since what version, but the one in Windows 10 version 1809 works) is mouse supoort. You can control the Midnight Commander using a mouse and even scroll using the mouse wheel.

SSH

Good passwords are long and/or complex and therefore almost by definition hard to type. Especially without feedback, i.e. when logging in to a remote system. The trick is to create a unique personal key and to copy that over to a remote machine once, which requires you to login. From that moment on you'll be "known" om the other machine and you'll be able to login from the computer you authorized in this way without typing in your password. Very secure, very convenient. And it enabled the Midnight Commander to set up a shell link without prompting for a password. This is how to do that for the Raspberry Pi but it works on other Linuxes. (Spoiler: it involves ssh-keygen and ssh-copy-id)

Tmux

I wanted to use my Raspberry Pi to perform scheduled tasks, one of which basically runs all day. You can do that with cron, but that's more for unattended tasks - there is no way to peek at the progress of the task, other than to do logging, and consulting the log every now and then. I was looking more for a Remote Desktop-like experience: I connect to the Pi, start a long running command, then disconnect from it. When I reconnect, I expect my terminal to pick up where it left off - rather, to have continued without me, ignoring the fact that I disconnected in the first place. Unfortunately, that's not how it works with ssh. When you disconnect, there is no way to reconnect. Your job may be left running, but there's no way to get to its real time outut anymore. Enter tmux.

Tmux solves this by setting up a sort of "Remote Desktop for Terminal Sessions" - but it is so much more. Basically, it's a window manager for terminals - it hosts any number of terminal windows in a terminal window. (I guess that's what the name means: it's a "Terminal Multiplexer"). When you start tmux on it's own, you get a simple bash prompt with a status bar at the bottom. Everything works more or less as you expect: tab completion, command history, you name it. The magic starts when you type a hot key (by default Ctrl+B) followed by more keys:

  • Ctrl+B c - creates a new window. The previous screen is replaced with a fresh bash prompt and the status bar at the bottom now shows two windows
  • Ctrl+B n - move to the next or previous (Ctrl+B p) window

That's great for doing two things at once. But there's more:

  • Ctrl+B " (that's the double quote) - splits the window into a top and bottom part
  • Ctrl+B % (percent sign) - splits the window into a left and right part

Now you can have two terminal sessions running side by side (or top to bottom). Switching between panes is easier to remember than switching between windows:

  • Ctrl+B arrow-key - yes, you can just use Up, Down, Left, and Right (after pressing Ctrl+B) to navigate panes

If your screen is large enough, you can do this multiple times, e.g. split an already-split window (called a "pane") again. This way, you can create really complex window layouts. One of my favorites is a normal, full size window running the Midnight Commander and a second window containing four panes with status information. And since tmux is configurable and scriptable, the Internet is full of peoples' favorite setups. Mine it not interesting enough to add ;-) One thing you should probably do is configure the mouse in tmux, so you can just click in a pane to activate it.

But how about the RDP-like experience? The trick is that you can disconnect from the session using:

  • Ctrl+B d - disconnect from tmux. Your windows will keep running in the background!

You will be dropped back into the terminal you used to start tmux. If you start tmux again, you will get a new session, so you'll have two. That can be useful, but in the RDP-like scenario you will want to start tmux with tmux a which "attaches" to a previous session. This is the real stuff: you can start a tmux session, set it up the way you want it, disconnect from, log offlog back in, re-attach to the session and Bob's once again part of your family.

Needless to say, you can do this with multiple sessions, i.e. you can have several sessions running, each containing multple windows, each split into multple parts, and connect to each whenever you want, even from multiple SSH clients. Magic. But then again, we use this kind of stuff on a daily basis and call it Windows or MacOS. It's just fascinating to see that a user installable program (i.e.sudo apt-get install tmux) can add these capabilities to Linux for text-based terminal sessions.

Now tmux is (like Linux I guess) somewhat geeky. You can do almost anything with it, but then you need to do some reading. Start with

  • Ctrl+B ? (question mark) - show key bindings. Press q to exit
  • man tmux - for more help

But the shortcuts mentioned above should get you up and running.

.NET on Linux

A separate topic is the availability of .NET Core on Linux. Using remote ssh from WSL it's easy to compile on your dev box, then copy the output to (for instance) a Raspberry Pi and test there. But maybe even more interesting is something built on .NET Core:

PowerShell for Linux

PowerShell 6.0 has been re-written using .NET Core, which makes it available on Linux! (and the Mac). It has been renamed to pwsh (Unix command names tend to be short, I guess), but the amazing thing is that it just works. There's even an SSH-based version of PowerShell Remoting.

Now why would you need this? Arguably, anything you can do with PowerShell van be done using bash or some combination of the myriad of tiny Linux programs available on every standard distribution (e.g. grep, awk, tail, ...). But the same can be said of the standard Windows command line, cmd. Yet on Windows I prefer PowerShell over cmd any day. Admittedly, cmd is nowhere as powerful as bash (though it's underestimated as well - you can do a lot with poor old cmd). But PowerShell is just cleaner, more flexible, and now: more portable than cmd. And those reasons are valid on Linux, too. Because however powerful and flexible bash plus Linux utilities are, their use is only somewhat standardized, making the resulting bash scripts difficult to understand and maintain.

Case in point: I wanted to upload something to Azure Blob Storage from a Raspberry Pi. There's a dedicated Azure Command Line available for Linux, but that wasn't ported to the Pi yet. And it has it's own learning curve, regarding authentication etc. The PowerShell script I used for Windows just ran on the Pi, as promised. And although I try to accomplish simple tasks in pure bash (because that's faster and requires less resources and hey, you gotta keep learning), I keep running into bash idiosyncracies like signifcant spaces. So I find myself using PowerShell for all but the most simple management tasks - and that's exactly what it's for, I guess. And once again, it even runs on the Raspberry Pi!