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 errors
      • set -u: treat references to undefined variables as errors
      • set -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 forces seq 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.

  • Lightfire228@pawb.social
    link
    fedilink
    arrow-up
    3
    arrow-down
    4
    ·
    edit-2
    18 hours ago

    just use python instead.

    • wrap around subprocess.run(), to call to system utils
    • use pathlib.Path for file paths and reading/writing to files
    • use shutil.which() to resolve utilities from your Path env var

    Here’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)
    
    • Matt
      link
      fedilink
      arrow-up
      1
      ·
      9 hours ago

      Or Rust. Use Command::new() for system commands and Path::new() for paths.

      • Lightfire228@pawb.social
        link
        fedilink
        arrow-up
        1
        ·
        edit-2
        18 hours ago

        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 for p to finish execution

        there 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'))