2

If symbolic link bar points to folder foo, then the two following commands have very different behaviors:

mv bar  baz   # rename link 'bar' to 'baz'
mv bar/ baz   # rename folder 'foo' to 'baz'

(tried with bash 3.2 and zsh 5.7.1 on OSX).

I find the second behavior very dangerous (especially given that "tab" usually completes folder names with a trailing slash), and would like to prevent it if possible. The manual of mv doesn't seem to have an option for that. Short of aliasing mv with my own function, is there maybe a Bash option or zsh option, or something else, that would help with this issue?

  • 2
    This comment will be useless but Linux coreutils mv doesn't have this issue: mv bar/ baz -> mv: cannot move 'bar/' to 'baz': Not a directory – Artem S. Tashkinov Jun 24 '20 at 15:51
  • @ArtemS.Tashkinov Good to know! Weirdly enough though, gmv (installed via brew install coreutils) has the same issue on my side. – Jonathan H Jun 24 '20 at 16:03
  • one way in Bash might be set mark-directories off in a INPUTRC file, but that setting applies to all directories, not just symlinks – LL3 Jun 24 '20 at 16:06
  • Maybe bash 3.2 is a bit too dated? I see it is from 2006. Not much helpful, but for the slash in autocomplete, you may disable it if your bash has mark-symlinked-directories option (look in man bash, "Readline Variables" section). – Quasímodo Jun 24 '20 at 16:06
  • 1
    The difference between OSes is down to the behavior of the rename system call, and this looks like a bug in Linux's rename. Why would rename("bar/", "baz") fail with ENOTDIR? It's supposed to be equivalent to rename("foo", "baz"). – Gilles 'SO- stop being evil' Jun 24 '20 at 17:15
1

In zsh, as long as you don't unsetopt auto_remove_slash, zsh removes the trailing slash at the end of completion. It adds a slash during completion, so that baTab results in bar/, but if you press Space (or move the cursor or most anything other than inserting something), zsh removes the /. (The / is highlighted to indicate that it's “on sufferance”.)

If you end up entering the command with a slash mv bar/ baz, renaming the directory foo is the expected behavior. Since bar/ ends with a slash, pathname resolution converts it to foo.

If you want to avoid this behavior, you'll have to write a wrapper function for mv. Untested, for zsh:

function mv {
  local i
  for ((i=1; i < $#; i++)); do
    if [[ $argv[$i] == *[^/]*/ ]]; then
      argv[$i]=${argv[i]%/}
    fi
  done
  command mv "$@"
}

Note that this function is fairly naive, for example it doesn't understand that -t/--target takes a destination as argument which shouldn't have its trailing slash stripped.

  • Thanks for that Gilles! I just disabled auto_remove_slash to avoid adverse effects with rsync, where trailing slash are actually meaningful; so that option is no good in my case, but might be helpful to others. It looks like the function you provided is a good place to start for an alias. – Jonathan H Jun 24 '20 at 18:34
  • I think you can override the effect of auto_remove_slash for a specific command with the appropriate zstyle. I don't know what the incantation is without research. – Gilles 'SO- stop being evil' Jun 24 '20 at 18:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.