1. 37
  1. 9

    This is something I wrote the other day. It requires nothing other than POSIX sh, [, printf, dd and stty. Implementing this was rather interesting as POSIX shell is very limiting (this is what makes it fun).

    I’ve also written a file manager in bash which you may have heard of. https://github.com/dylanaraps/fff

    I hope you find this interesting/enjoy using it. Happy to answer any questions too. :)

    1. 1

      Interesting use of dd for input. It took me longer than I like to admit to figure out how/where it actually reads in the files, TIL:

      for name [ in word … term ] do list done where term is either a newline or a ;. For each word in the specified word list, the parameter name is set to the word and list is executed. If in is not used to specify a word list, the positional parameters (”$1”, “$2”, etc.) are used instead.


      set (…) [option]] [±name] [–] [arg …] The set command can be used to set (-) or clear (+) shell options, set the positional parameters, or set an array parameter. (…) Remaining arguments, if any, are positional parameters and are assigned, in order, to the positional parameters (i.e., 1, 2, etc.).

      IE: on: https://github.com/dylanaraps/shfm/blob/master/shfm#L257

      set – *

      arguments are set up so that they contain the results of glob expansion (no hidden files handling..) - then explicitly passed on: https://github.com/dylanaraps/shfm/blob/master/shfm#L267

      redraw “$@”

      to be iterated over by: https://github.com/dylanaraps/shfm/blob/master/shfm#L147

      for file do (…)


      1. 1


        Interesting use of dd for input.

        This is one of the only(?) portable methods of reading input a single byte(char?) at a time. Bash’s read supports single character input which is really nice.

        arguments are set up so that they contain the results of glob expansion (no hidden files handling..) then explicitly passed on

        This is the closest thing to an array one has access to in POSIX shell (short of creating a string and using some delimiter). You are also limited to one “list” at a time! Scoping is also weird as functions cannot modify the parent’s “list”. This is why input handling and anything needing to modify the list is scoped “globally” (in main()).

        for file do (…)

        This is really fun syntax. Looks especially cool when used with a case statement.

        for file do case $file in
            name1) : do thing ;;
            name2) : do other thing ;;
        esac done

        Functions in POSIX shell can also be defined in the following ways:

        func() echo hi
        func() (echo hi) # code runs in subshell, variables all locally scoped
        func() if ...
        func() for ...
        func() while ...

        This does /not/ work in bash funny enough.

        1. 1

          This is the closest thing to an array one has access to in POSIX shell (short of creating a string and using some delimiter).

          There’s always text “files” via fifos or pipes (or temp files) - which may or may not be better than (potentially very large) argument arrays (eg: navigating a maildir in this fashion).


          for f in *
            echo "${f}" >> "${cd_list}"
          redraw # pass in $cd_list or use as global

          But then it’d make more sense to use find, zero-terminated names (the above doesn’t handle filenames with newlines) - and at that point you can pipe through sort and do all kinds of things.

          I lean heavily towards using streams of text for shell scripts - because using text “files” as the “Turing tape” is generally preferable to the rather anemic data types shell provides.

          But at any rate - now I know another shell idiom I’ll strive to avoid using ;)

          Ed: oh, BTW-please don’t look too closely at the code snippet - the IFS can play havoc with that loop. Say with filenames with tabs or spaces… The point was more about storing the list, than generating the list…

      2. 1

        I’m curious: the readme (and this comment) say it depends on [, but the readme also says implementation uses case everywhere to avoid a dependency on [

        1. 1

          Nice catch. I was avoiding [ at the beginning. Later I had to make use of it (to see if entry is a directory or not, etc). Will fix the README, thanks.

      3. 1

        This looks like nice work. Can you run down when you might prefer to use this versus fff versus ranger? I feel like most times where I’d want this, I’ve got curses and python, so I’d just use ranger. Which makes me think I’m missing something.

        1. 4

          This is great for smaller environments which may not have bash, ncurses, python, etc. There’s a Linux distribution that will be putting it in their initrd for example. It’s also good for environments where disk space is limited. I also personally no longer use bash so I’ve ported everything I use over to POSIX shell or C.

          Edit: Should also mention that it’s all contained in a single file and there’s no compilation step necessary!

          1. 1

            There’s a Linux distribution that will be putting it in their initrd for example.

            Just curious, which Linux distro would that be? :)

            1. 1

              Including it in initrd hadn’t occurred to me. It seems especially useful for that.

              Does it work well with the ash that busybox ships?

              1. 1

                Yes it does. It should work in all POSIX shells. If not, that’s a bug. :)

            2. 2

              Don’t know if Dylan will agree, but I like to think it’s less about “needing” this, and more about how creative you can get with limited resources—Creative limitation. It’s a really fun exercise in programming. For me, personally, I like trying to (re)write most of my tooling in pure bash—no external programs called whatsoever. And shfm, I feel, was something born out of the idea that fff (pure bash) could possibly be simplified further (POSIX sh).

              1. 3

                The need is deceivingly greater than it might occur to most people. Many systems don’t have much of what the average shell user is used to. NASes, plug computers, microcomputers, or even containers. Buying into tens or even hundreds of megabytes of dependencies always comes with considerable costs. An usually ignored downside of dependencies is their hit on software longevity.

                This adds a great deal of ergonomics and convenience in the form of a simple text file, that works has tiny dependencies. Even ncurses is left outside the dependency list. If you are in an unix shell and can open a socket to a remote internet location, you can use this. And most likely will be able to do so for many years to come.

            3. 1

              Nice! I love the controls, very usable. I will probably use it actually.

              I had written something a little similar, shimpr, POSIX shell presentation tool. For some things about managing input and term geometry it seems to reduce to similar solution, but you have avoided extended tput for example. I was not aware of its limitations actually.

              And good to know that SIGWINCH is supposed to become POSIX.

              1. 1

                Why “maybe” file operations?

                1. 2

                  Depends on how feasible the feature is to implement. Multi-file operations are 100% out of the equation as I only have access to one list at a time (and it is used for the current directory’s contents). Single file operations aren’t really very useful (easier to hit the ! key, open a shell and type your command out).

                  A temporary file could be used here to store each file though the utility would then need to mess around with temporary files. I’m unsure if I want to go this route.

                  Basically; need to see if it is feasible, need to think about where I want to limit or draw the line for this utility.

                  Edit: Should note. Single file operations are very handy for dealing with filenames which contain non-English characters (or newlines, etc).

                  1. 2

                    I usually miss having a file manager when I would like to delete a file that has weird characters I can’t type in the terminal.

                    1. 2

                      Oh yeah, this is one of my use-cases for it too. I deal with a lot of Greek and Japanese filenames.

                2. 1

                  Incredible this is built in 500 lines of posix shell code.


                  EDIT: Super cool it exits back into shfm after exiting e.g. vim. And / to search, nice