1. 34

I don’t know why it took me so long to create this, but from here on out, it’s going on all my machines and I’ll use it a few times per week at work.

alias spaces2underscores='for i in *; do mv -iv "$i" "${i// /_}"; done'


hobbes@metalbaby:~/projects/apache/20180119-brianna$ spaces2underscores 
renamed 'LHD Map Jan 2018.pdf' -> 'LHD_Map_Jan_2018.pdf'
renamed 'Contacts List.xls' -> 'Contacts_List.xls'

Previously, I would do emacs . followed by C-x C-q M-x replace-string SPC _ RET C-c C-c C-x C-c, which accomplishes the same goal, and felt amazing when I did it to hundreds of files at a time. Of course, I’ll still use this method if the replacement is more complex than SPC -> _

    1. 12

      I would rewrite this as a shell function:

      spaces2underscores() {
         for i in "$@"; do
            mv -iv "$i" "${i// /_}";


      spaces2underscores *
      spaces2underscores */*.txt

      It’s also easy to make the default *:

      if test $# -eq 0; then
         set -- *  # set "$@" to all files in the current directory

      The bash manual even says that functions can do everything aliases can do, and are preferred.

      Aliases are a bit hacky from an implementation point of view and don’t compose as nicely. Shell aliases and functions are somewhat analogous to C macros and C functions – aliases are a lexical construct, not really part of the language proper. There’s a somewhat confusing rule about trailing spaces in aliases.

      That said, I am resigned to implementing aliases in Oil, because even I have aliases in my .bashrc from 10 years ago ! :-/

      1. 3

        Yes, my alias doesn’t take an argument. That’s an oversight… Thanks idea, args will be useful!

        You’re right, functions > aliases.

        Using a function rather than an alias gives me opportunity to more easily evolve the thing over time, to handle new cases I hadn’t considered… Like this one:

        "A very important document - FINAL.docx"

        Naively that would become:


        …But I’d prefer:


        Since that construct is common, why not address it? I’ll just do a second replacement with "_-_" as the match pattern.

        It’s done easily enough as an alias…<– NOPE

        Okay, a function:

        function spaces2underscores() {
          # globs don't need to be wrapped in quotes, right??
          # I couldn't make it work with the quotes..
          for i in ${@:-*' '*}; do # if "$@" isn't set, set it to "all the files with spaces"
            s2u_temp="${i// /_}"
            mv -iv "$i" "${s2u_temp//_-_/-}";

        For anything much more complicated, I’d just do it manually with dired or simply mv.

      2. 1

        And here I use alias to fix common typos I make:

        alias mroe=more
        alias maek=make
        alias amke=make
        alias rm='echo That command is not available'

        That last one is to remind me to double check what I’m about to delete.

        1. 1

          Why not just rm=“rm -i”?

          1. 1

            I would find that exceedingly annoying. There are times when I delete thousands of files (generated data for test cases that need to be regenerated between runs because the tests modify the files).

            1. 2

              So use rm -f if you dont want the confirmation.

              How does making it unavailable to type easily work?

              If youre using GNU rm, theres also rm -I (capital I) which will prompt once for >3 files

    2. 9

      Note that in dired you can do C-c C-q to make the buffer editable, rename or modify the files however you want, then commit the changes via C-c C-c

      1. 2

        Yes. I describe that in my final paragraph, although I use C-x C-q, since C-c C-q doesn’t work here.

        Hot editing the directory listing itself is brilliant, the first time I did it was certainly one of those mind-expanding moments that emacs has so many of.

        EDIT: oh I get it now, you were explaining my final paragraph for me… Thank you. :)

    3. 8

      For those, like me who were wondering what dired even is:

      “Dired makes an Emacs buffer containing a listing of a directory, and optionally some of its subdirectories as well. You can use the normal Emacs commands to move around in this buffer, and special Dired commands to operate on the listed files.”


    4. 4

      Love dired & wdired! Emacs magic at its best.

      If I’m on the command line I often go for the humble Perl-based rename utility


      I think the equivalent command is

      rename 's/ /_/g' *

      (Can’t remember if you need the g modifier or not, I’m not near my laptop to check…)

    5. 3

      I had no idea dired had an editable mode! That’s a game-changer for me.

      1. 3

        Had a similar game-changer moment last week when I learned there are modes to make the grep results buffer editable:


        (And similar modes exist for things like helm-ag, etc)

        1. 2

          Yeah, I’ve been using those for a while. Never fails to impress colleagues :-)

    6. 3

      Well, in case you are in Emacs, finding a faster way is as simple as…

      (defun spaces-to-underscores()
        (replace-string " " "_"))

      And then bind it to a key that you like. The perks of the modular editor!

    7. 2

      I’m using this: https://paste.xinu.at/HQx/ I call it remove_special

    8. 2

      If you’re already an Emacs user you probably know this, but note that you can use dired and eshell via tramp, meaning you can do all those things without leaving you local Emacs instance.

    9. 1

      Speaking of bash aliases, they can be great fun.


    10. 1

      What is the magic behind ${i// /_}"

      1. 3

        It’s bash parameter substitution – very useful!

    11. 1

      The last file manager I used that wasn’t a command line was Directory Opus Magellan II on the Amiga, twentyish years ago. Nothing has compared yet, and I’m too comfortable and too fast now with the shell to go back.

    12. 1

      Alternatively, in zsh:

      zmv -n '*' '${f// /_}'

      (This will also ensure the replacement’s don’t overlap.)

    13. 1

      One snafu with aliases that I never found a great work-around was using date or cal or anything that is mutable, would be sort of galvanized after that bash/zsh profile was set. One would have to source that profile file to get the current, in my case, time. So, it didn’t work for things like that (which was quite a few aliases). A quick hack to make this work is welcomed, BTW.

      1. 2

        I’m not sure what you mean. I tried to guess, but I ended up with dynamic date values. I’ll attempt to fix a galvanized example you give me…

        hobbes@metalbaby:~/e2-scratch/s2u$ alias foo='echo $(date)'
        hobbes@metalbaby:~/e2-scratch/s2u$ foo
        Fri Jan 19 17:48:51 CST 2018
        hobbes@metalbaby:~/e2-scratch/s2u$ foo
        Fri Jan 19 17:48:53 CST 2018
        hobbes@metalbaby:~/e2-scratch/s2u$ foo
        Fri Jan 19 17:48:54 CST 2018
        hobbes@metalbaby:~/e2-scratch/s2u$ foo
        Fri Jan 19 17:48:55 CST 2018