Explanation for newbies:
-
Shell is the programming language that you use when you open a terminal on linux or mac os. Well, actually “shell” is a family of languages with many different implementations (bash, dash, ash, zsh, ksh, fish, …)
-
Writing programs in shell (called “shell scripts”) is a harrowing experience because the language is optimized for interactive use at a terminal, not writing extensive applications
-
The two lines in the meme change the shell’s behavior to be slightly less headache-inducing for the programmer:
set -euo pipefail
is the short form of the following three commands:set -e
: exit on the first command that fails, rather than plowing through ignoring all errorsset -u
: treat references to undefined variables as errorsset -o pipefail
: If a command piped into another command fails, treat that as an error
export LC_ALL=C
tells other programs to not do weird things depending on locale. For example, it forcesseq
to output numbers with a period as the decimal separator, even on systems where coma is the default decimal separator (russian, dutch, etc.).
-
The title text references “posix”, which is a document that standardizes, among other things, what features a shell must have. Posix does not require a shell to implement
pipefail
, so if you want your script to run on as many different platforms as possible, then you cannot use that feature.
just use python instead.
subprocess.run()
, to call to system utilspathlib.Path
for file paths and reading/writing to filesshutil.which()
to resolve utilities from yourPath
env varHere’s an example of some python i use to launch vscode (and terminals, but that requires
dbus
)from pathlib import Path from shutil import which from subprocess import run def _run(cmds: list[str], cwd=None): p = run(cmds, cwd=cwd) # raises an error if return code is non-zero p.check_returncode() return p VSCODE = which('code') SUDO = which('sudo') DOCKER = which('docker') proj_dir = Path('/path/to/repo') docker_compose = proj_dir / 'docker/' windows = [ proj_dir / 'code', proj_dir / 'more_code', proj_dir / 'even_more_code/subfolder', ] for w in windows: _run([VSCODE, w]) _run([SUDO, DOCKER, 'compose', 'up', '-d'], cwd=docker_compose)
Or Rust. Use Command::new() for system commands and Path::new() for paths.
Cool, now pipe something into something else
that is a little more complicated
p.communicate()
will take a string (or bytes) and send it to the stdin of the process, then wait forp
to finish executionthere are ways to stream input into a running process (without waiting for the process to finish), but I don’t remember how off the top of my head
from shutil import which from subprocess import Popen, PIPE, run from pathlib import Path LS = which('ls') REV = which('rev') ls = run([LS, Path.home()], stdout=PIPE) p = Popen([REV], stdin=PIPE, stdout=PIPE) stdout, stderr = p.communicate(ls.stdout) print(stdout.decode('utf-8'))