Effective Bash: I Know It Hurts but Put Your Pipes and Logical Operators at the End of the Line
Despite how nice it looks to have the pipes line up on the left or to see the logical operators at the beginning of what they protect, the backslashes at the end of line are extremely sensitive to what follows them so if you're working in any context other than a rich text editor that knows only to place a single newline after them you'll screw up your ability to copy and paste the text into a terminal safely. It doesn't take long to get used to writing this way and it also works very well interactively.
# Good git ls-files -z | xargs -0 cat | sha256sum # Bad git ls-files -z \ | xargs -0 cat \ | sha256sum # Bad git ls-files -z | xargs -0 cat | sha256sum # Good { cd dir && git sync } || exit # Bad { cd dir \ && git sync } || exit # Bad { cd dir && git sync } || exit
A really nice trick if you're using a sane OS is to construct your example commands interactively at the prompt and then lean on rectangular selection to extract it for your text.
$ { > echo foo > echo bar > echo bat > } | > sed 's|a|charnock|' foo bcharnockr bcharnockt
Say what you will about it. It's the shell of the world (unless you've gone mad). Being effective in it means that I can accomplish almost anything that's possible to accomplish via the CLI on any system I'm on without access to (many) external tools or languages. It was purpose built to shell out, consume STDIN, construct new commands out of disparate pieces, and continue on. It's a paragon of terseness if that's what you're trying to do. If you're trying to do pretty much anything else, look elsewhere.
I write nearly everything in a terminal emulator in GNU Emacs, from software to blog posts to journal entries. I prefer reading in Emacs as well if only because I have access to all my usual search tools and navigational keys. This means that I'm a bit obsessive about plain text formatting where others let their WYSIWYG editors line wrap for them. I'm especially sensitive to code blocks in documentation like README's and how they're formatted. Most people seem to just wrap their code in a code block and be done with it, trusting that eventually someone will view it in a browser which will take care of the formatting for them.
This is a terrible restriction on where you can view your documentation or your shell scripts and removes your ability to use standard *nix tooling to extract the scripts safely and apply them in a terminal.
Years ago when I was working at a large python startup I was complaining to the Chief Architect about how it was hard to keep my lines under the 120 character limit they imposed because python is whitespace sensitive and I prefer descriptive names for my functions and a function decomposition that follows the *nix/clean code philosophy.
def a_long_descriptive_name(a, b, c): pass def another_even_more_descriptive_name(d, e, f): pass def boy_howdy_can_you_tell_i_was_a_java_dev_and_still_pretty_much_am_QMARK(g, h, i): pass calling_my_functions_all_together(a_long_descriptive_name(1, 1434, 14), another_even_more_descriptive_name("blah", "boo", "foo"), boy_howdy_can_you_tell_i_was_a_java_dev_and_still_pretty_much_am_QMARK(True, False, True))
He pointed out that python natively (like, at the parser), supports the idea of list continuation in it's syntax.
x = [1,2,3] y = [1, 2, 3,] x == y # => True
def a_long_descriptive_name(a, b, c): pass def another_even_more_descriptive_name(d, e, f): pass def boy_can_you_tell_i_was_a_java_dev_and_still_pretty_much_am_QMARK( g, h, i): pass calling_my_functions_all_together( a_long_descriptive_name(1, 1434, 14), another_even_more_descriptive_name("blah", "boo", "foo"), boy_can_you_tell_i_was_a_java_dev_and_still_pretty_much_am_QMARK( True, False, True))
is totally valid, not altogether unreadable, and requires no \ trickery.
It was recalling this that lead me to the realization that bash has similar parser level support for informing it that you're not quite done typing the command out: control operators. While the definition is useful you can seem them in action in the manual in the Lists of Commands entry.
Specifically, if your command as presented to bash does not end in a newline, &, or ; (and ;; sometimes), bash natively understands that you mean to keep telling it what to do and presents you with your $PS2. To see this in action:
$ { > echo foo > echo bar > echo bat > } | > sed 's|a|charnock|' foo bcharnockr bcharnockt
Other tools have similar behavior:
$ python3 Python 3.7.6 (default, Dec 30 2019, 19:38:28) [Clang 11.0.0 (clang-1100.0.33.16)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> x=[1,2,3] >>> y=[1, ... 2, ... 3, ... ] >>> x == y True >>> $ irb irb(main):001:0> x=[1,2,3] => [1, 2, 3] irb(main):002:0> y=[1, irb(main):003:1* 2, irb(main):004:1* 3,] => [1, 2, 3] irb(main):005:0> x == y => true irb(main):006:0> user=> (def x [1 2 3]) #'user/x user=> (def y [1 2 3]) #'user/y user=> (= x y) true user=>
As you can see this knowledge is generally useful in most dynamic contexts.
One thing that never occurred to me though is that with proper rectangle selection support (you're using a sane window manager right?) the marriage of this feature with that lets you construct an example at the CLI and then copy/paste it easily into your editor. Look at any of the examples above and you can easily see the rectangle you would extract.
This is yet another reason why why your PS1 should absolutely terminate in a single '^\$', whatever else precedes it. The fact that ruby and python both have their PS2's constructed to be identical in textual length to their PS1 I think is proof enough that I'm not the first person to realize this.
In case you're worried that this will make using your history search or completion facilities harder, don't be. In bash at least (I can't speak to the other interpreters just now), setting the cmdhist shopt tells bash to attempt save the multiline command you entered as a single history entry for later search and execution.