1. 9

  2. 8
    [grahamc@hyperchicken:~]$ which bash
    [grahamc@hyperchicken:~]$ /bin/bash
    bash: /bin/bash: No such file or directory
    [grahamc@hyperchicken:~]$ command which bash
    [grahamc@hyperchicken:~]$ builtin command which bash
    [grahamc@hyperchicken:~]$ type which
    which is a function
    which () 
        ( echo "/bin/bash" )
    [grahamc@hyperchicken:~]$ type command
    command is a function
    command () 
        ( echo "/bin/bash" )
    [grahamc@hyperchicken:~]$ type builtin
    builtin is a function
    builtin () 
        ( echo "/bin/bash" )

    and don’t forget absolute paths can be functions too:

    [grahamc@hyperchicken:~]$ /run/current-system/sw/bin/bash --version
    zsh 5.8 (x86_64-pc-linux-gnu)
    [grahamc@hyperchicken:~]$ type /run/current-system/sw/bin/bash
    /run/current-system/sw/bin/bash is a function
    /run/current-system/sw/bin/bash () 
        ( zsh "$@" )

    although at this point I’m starting to suspect type.

    1. 2

      and don’t forget absolute paths can be functions too

      That blew my mind! I’m often packaging badly written scripts that rely on absolute paths to external tools, and was wondering if I can use this trick to make some of them work. I wonder if I can export those functions to child processes. export -f doesn’t like it though :(

      ❯ /some/dir/lol() {
      > }
      ❯ /some/dir/lol
      ❯ export -f /some/dir/lol
      bash: export: /some/dir/lol: cannot export
      ❯ export -f "/some/dir/lol"
      bash: export: /some/dir/lol: cannot export

      EDIT: I managed to export it with declare -fx, but then child shells started yelling at me. I guess it doesn’t work. Oh well…

      ❯ declare -fx /some/dir/lol
      ❯ bash
      bash: error importing function definition for `/some/dir/lol'

      EDIT2: This StackOverflow post uses declare -pf to “transport” a function to a remote host over SSH, and it’s perfectly usable for crappy local scripts. This definitely goes into my bag of tricks (hopefully somewhere deep!):

      ❯ cat /tmp/bad-script 
      ❯ bash -c "$(declare -pf /some/dir/lol); . /tmp/bad-script"
    2. 4

      Bash has a handy POSIX mode which you can run your code under if you’re not sure if it’s portable or not. This also prevents user code from re-defining builtin commands defined in POSIX.

      If we want to get all technical about it, then overriding echo should be allowed according to POSIX, as it’s not a “special built-in”; the only special built-ints are break, :, continue, ., eval, exec, exit, export, readonly, return, set, shift, times, trap, and unset (and there are also some reserved keywords, like if, for, etc.)

      This came up years ago with the FreeBSD /bin/sh as well.


      Unless you also redefine the command command, then you’re really screwed.

      (exec echo "lol?")

      1. 3

        Just because you can, doesn’t mean you should.

        1. 3

          This recently came up on the mailing list and the latest version of the bash manual provides better clarity on what is supported:

          When in posix mode, fname must be a valid shell name and may not be the name of one of the POSIX special builtins. In default mode, a function name can be any unquoted shell word that does not contain $.

          Part of the source code that checks for valid functions is here

          1. 2

            or, how to write unreadable bash scripts :)

            1. 2

              Once upon a time I made a giant pile of scripts, where I used the following functions for logging to stdout, logfile, stdout+logfile,

              function ❱() {
              function ❱❱() {
              function ❱❱❱() {

              In practice, this quickly became very annoying though, as every time I wanted to call those functions, I had to go copy-paste the function name from somewhere else. Stopped using “clever” names after that

              1. 1

                A shell script name, like any program name, can be any path not containing ‘/’ or ‘\0’. Shell functions are largely faster versions of scripts (except things that like “cd” change aspects of the current process). ‘[’ used to be an external command.