"secret" not in the sense that it's hidden, but that most people won't know about it. Because approximately nobody actually reads man pages in their entirety, they just get in to find out how a specific flag works and then get out.
I still struggle with the fact that I was (or delusional) an effective C programmer "back in the day" (before google etc) and all we had was "man" to look up std/x11 system calls.
Now I am dismayed with juniors who can't even be bothered to use google (or llms) to look up stuff on their first hiccup.
`pgrep`/`pkill` HATE this trick! Learn how this renegade developer (relatively) easily exits their hanging SSH sessions without restarting their laptop.
I've been using SSH for ~15 years and never knew about these escape sequences. I'm eagerly awaiting my next hung session so that I can test `~.`. It's much nicer than my current approach of having to close that terminal window.
> It's much nicer than my current approach of having to close that terminal window.
You can also just kill the ssh process (say from another terminal). That way you get to keep your terminal window. And this works with everything "blocking" your terminal, not just ssh.
If hung SSH connections are common it's likely due to CGNAT which use aggressively low TCP timeouts. e.g. I've found all UK mobile carriers set their TCP timeout as low as 5 minutes. The "default" is supposed to be 2 hours, you could literally sleep your computer, zero packets, and an SSH connection would continue to work an hour later, and generally speaking this is still true unless CGNAT is in the way.
If you are interested there are a few ways you can fix this:
Easiest is to use a VPN, because the VPN's exit node becomes the effective NAT they usually have normal TCP timeouts due to being less resource constrained. Another nice benefit of this method is you can move between physical networks and your connection doesn't die... If you use Tailscale then you already have this in a more direct way.
Another is to tune the tcp_keepalive kernel parameters. Lowering the keepalive timeout to be less than the CGNAT timeout will cause keepalive probes to prevent CGNAT from dropping the connection even while your SSH connection is technically idle. For Linux I pop these into /etc/sysctl.d/z.conf, I have no idea for Windows or Mac:
This is really a misuse of these settings, they are supposed to be for checking TCP connections are still alive and clearing them up from the local routing table. Instead the idea is to exploit the probes by sending them more frequently to force idle connections to stay alive in a CGNAT environment (dont worry the probes are tiny and still very infrequent).
_time=240 will send a probe after 4 mins of idle connection instead of the default 2 hours, undercutting the CGNAT timeout. _intvl=60 and _probes=120 mean it will send 120 probes 60 seconds apart (2 hours worth) before considering the connection dead. This will keep it alive for at least 2 hours, but also allows us to have the best of both worlds so that under a nice NAT it keeps the old behaviour, e.g if I temporarily lose my network the SSH connection is still valid after 2 hours, but under CGNAT it will at least not drop the connection after 5 mins so long as I keep my computer on and don't lose the network.
There are also some SSH client keepalive settings but I'm less familiar with them.
I last used this menu about 20 years ago when a dialup modem was the only way to roll, and have pretty much forgotten about it since the days of always-on direct to the desktop TCP/IP ..
Hidden or undocumented features like this always have a strange appeal. Part of it is nostalgia for older software where small Easter eggs or experimental features would sometimes ship in production builds.
Wow, never knew this... That said I'm not sure if I'll remember using it as my muscle memory is already trained to kill hung processes via ctrl-b s, ctrl-k, ctrl-b x (in tmux).
I've been using the ~. shortcut for a while, but somehow escaped learning about the help menu.
Another neat thing I noticed while playing with it just now: there's an option to enter ~ twice to send a literal ~, but usually you don't have to do this when typing something like 'ls ~' in a regular session. Not only does the ~ have to be the first character on a line to start an escape sequence, but typing on a line, backspacing all the way to the start and then typing ~ also sends a literal tilde. It only triggers the escape sequence if the ~ is the chronologically first character after a newline (or first in the session), which is an unlikely thing to type into a shell in a normal session. Good choice of UI, both the character and the state machine.
it starts with a pretty common char, but almost never gets in the way to the point I forget it exists. Meanwhile docker -t uses ^P which I use all the time for history instead of arrow keys. It's possible to configure it, but it's not worth the hassle on servers. Really, really annoying.
This is not specific to ssh. Telnet and rlogin have similar things with ~ as the escape character. Back in the day it was common to send BREAK and other escape sequences when you were hardwired.
I intended to post in defense of manpages. I love manpages. I think most open-source manpages are high-quality, and a few are really outstanding.
"It's easier to ask an AI" can be true without implying that manpages are bad.
However, "man" the tool does have issues, and one of them bit me just now.
So, I didn't know about openssh client escapes like ~?. I thought, "surely that's in the manpage?" I opened the manpage (in less) and searched for "\~\?". No hits.
Of course, escape characters are documented in the manpage, and the string "~?" does appear. Why didn't search find it? Because man, in its infinite wisdom, decided to render every instance of "~" as some bizarre unicode not-tilde, which is visually similar but totally impossible to grep for.
This has also bitten me in the past with dash. DASH. A character that is critically important when documenting invocation options. man loves to convert it into something that looks like dash, prints like dash, but doesn't come up in search.
I'm sure there is a way to turn this "feature" off, and I'm about to spend a bunch of time figuring out what it is. But this is documentation for command-line tools. Silently destroying our ability to grep it should NOT be the default.
Manpages are both good and awful. If you already know how to use the command and you just need a refresher on how to enable the diagnostic option for wolves, they're fine. OTOH if you don't know how to use the command they're terrible, just an endless catalogue of incomprehensible options scrolling past. Problem is that that's what Dennis and Ken created in 1929 for use on ASR-33s and it hasn't been updated since. 99% of the time when I need to use some new unfamiliar command I go to whatever DDG pops up, usually StackOverflow, for a guide on usage.
59 comments
[ 0.21 ms ] story [ 47.9 ms ] threadProxyCommand is fun
Jackpot if they're just a pointer to an 'info' page.
Now I am dismayed with juniors who can't even be bothered to use google (or llms) to look up stuff on their first hiccup.
#include <old-man-shouting-at-clouds>
I find it convenient not to have to worry about accidentally entering escape characters. YMMV.
You can also just kill the ssh process (say from another terminal). That way you get to keep your terminal window. And this works with everything "blocking" your terminal, not just ssh.
If you are interested there are a few ways you can fix this:
Easiest is to use a VPN, because the VPN's exit node becomes the effective NAT they usually have normal TCP timeouts due to being less resource constrained. Another nice benefit of this method is you can move between physical networks and your connection doesn't die... If you use Tailscale then you already have this in a more direct way.
Another is to tune the tcp_keepalive kernel parameters. Lowering the keepalive timeout to be less than the CGNAT timeout will cause keepalive probes to prevent CGNAT from dropping the connection even while your SSH connection is technically idle. For Linux I pop these into /etc/sysctl.d/z.conf, I have no idea for Windows or Mac:
This is really a misuse of these settings, they are supposed to be for checking TCP connections are still alive and clearing them up from the local routing table. Instead the idea is to exploit the probes by sending them more frequently to force idle connections to stay alive in a CGNAT environment (dont worry the probes are tiny and still very infrequent)._time=240 will send a probe after 4 mins of idle connection instead of the default 2 hours, undercutting the CGNAT timeout. _intvl=60 and _probes=120 mean it will send 120 probes 60 seconds apart (2 hours worth) before considering the connection dead. This will keep it alive for at least 2 hours, but also allows us to have the best of both worlds so that under a nice NAT it keeps the old behaviour, e.g if I temporarily lose my network the SSH connection is still valid after 2 hours, but under CGNAT it will at least not drop the connection after 5 mins so long as I keep my computer on and don't lose the network.
There are also some SSH client keepalive settings but I'm less familiar with them.
https://mosh.org/
https://en.wikipedia.org/wiki/Berkeley_r-commands
The good old times!
Anyway, if you try it from shell prompt it is likely will not work as pressing ENTER shows the next prompt. Try `cat` followed by ENTER and then ~?
I really hate it when people just rename terms. It made it harder to search properly for better answers.
Another neat thing I noticed while playing with it just now: there's an option to enter ~ twice to send a literal ~, but usually you don't have to do this when typing something like 'ls ~' in a regular session. Not only does the ~ have to be the first character on a line to start an escape sequence, but typing on a line, backspacing all the way to the start and then typing ~ also sends a literal tilde. It only triggers the escape sequence if the ~ is the chronologically first character after a newline (or first in the session), which is an unlikely thing to type into a shell in a normal session. Good choice of UI, both the character and the state machine.
~.
I guess you could call it a "secret" or at least "not super-well known (to people who aren't Linux 'experts')."
What I now have: ~B
What I really need: a way to stop long-running SSH connections from freezing
"It's easier to ask an AI" can be true without implying that manpages are bad.
However, "man" the tool does have issues, and one of them bit me just now.
So, I didn't know about openssh client escapes like ~?. I thought, "surely that's in the manpage?" I opened the manpage (in less) and searched for "\~\?". No hits.
Of course, escape characters are documented in the manpage, and the string "~?" does appear. Why didn't search find it? Because man, in its infinite wisdom, decided to render every instance of "~" as some bizarre unicode not-tilde, which is visually similar but totally impossible to grep for.
This has also bitten me in the past with dash. DASH. A character that is critically important when documenting invocation options. man loves to convert it into something that looks like dash, prints like dash, but doesn't come up in search.
I'm sure there is a way to turn this "feature" off, and I'm about to spend a bunch of time figuring out what it is. But this is documentation for command-line tools. Silently destroying our ability to grep it should NOT be the default.