• Home

  • Custom Ecommerce
  • Application Development
  • Database Consulting
  • Cloud Hosting
  • Systems Integration
  • Legacy Business Systems
  • Security & Compliance
  • GIS

  • Expertise

  • About Us
  • Our Team
  • Clients
  • Blog
  • Careers

  • VisionPort

  • Contact
  • Our Blog

    Ongoing observations by End Point Dev people

    Shell efficiency: mkdir and mv

    Jon Jensen

    By Jon Jensen
    March 10, 2017

    Little tools can be a nice improvement. Not everything needs to be thought-leaderish.

    For example, once upon a time in my Unix infancy I didn’t know that mkdir has the -p option to make intervening directories automatically. So back then, in order to create the path a/b/c/ I would’ve run:

    mkdir a; mkdir a/b; mkdir a/b/c
    

    when I could instead have simply run:

    mkdir -p a/b/c
    

    In working at the shell, particularly on my own local machine, I often find myself wanting to move one or several files into a different location, to file them away. For example:

    mv -i ~/Downloads/Some\ Long\ File\ Name.pdf ~/some-other-long-file-name.tar.xz ~/archive/new...
    

    at which point I realize that the subdirectory of ~/archive that I want to move those files into does not yet exist.

    I can’t simply move to the beginning of the line and change mv to mkdir -p without removing my partially-typed ~/archive/new....

    I can go ahead and remove that, and then after I run the command I have to change the mkdir back to mv and add back the ~/archive/new....

    In one single day I found I was doing that so often that it became tedious, so I re-read the GNU coreutils manpage for mv to see if there was a relevant option I had missed or a new one that would help. And I searched the web to see if a prebuilt tool is out there, or if anyone had any nice solutions.

    To my surprise I found nothing suitable, but I did find some discussion forums full of various suggestions and many brushoffs and ill-conceived suggestions that either didn’t work for me or seemed much overengineered.

    The solution I came up with was very simple. I’ve been using it for a few months and am happy enough with it to share it and see if it helps anyone else.

    In zsh (my main local shell) add to ~/.zshrc:

    mkmv() {
        mkdir -p -- "$argv[-1]"
        mv "$@"
    }
    

    And in bash (which I use on most of the many servers I access remotely) add to ~/.bashrc:

    mkmv() {
        mkdir -p -- "${!#}"
        mv "$@"
    }
    

    To use: Once you realize you’re about to try to move files or directories into a nonexistent directory, simply go to the beginning of the line (^A = control-A in standard emacs keybindings) and type mk in front of the mv that was already there:

    mkmv -i ~/Downloads/Some\ Long\ File\ Name.pdf ~/some-other-long-file-name.tar.xz ~/archive/new...
    

    It creates the directory (or directories) and then completes the move.

    There are a few important considerations that I didn’t foresee in my initial naive implementation:

    • Having the name be somethingmv meant less typing than something requiring me to remove the mv.
    • For me, it needs to support not just moving one thing to one destination, but rather a whole list of things. That meant accessing the last argument (the destination) for the mkdir.
    • I also needed to allow through arguments to mv such as -i, -v, and -n, which I often use.
    • The -- argument to mkdir ensures that we don’t accidentally end up with any other options and that we can handle a destination with a leading - (which does occasionally come up).
    • The mv command needs to have a double-quoted "$@" so that the original parameters are each expanded into double-quoted arguments, allowing for spaces and other shell metacharacters in the paths. (See the zsh and bash manpages for details on the important difference in behavior of "$@" compared to "$*" and either of them unquoted.)

    This doesn’t support GNU extensions to mv such as the option --target-directory that precedes the source paths. I don’t use that interactively, so I don’t mind.

    Because this is such a small thing, I avoided for years bothering to set it up. But now that I use it all the time, I’m glad I have it!

    shell tips


    Comments