Shell scripting: Why does only the “-” hyphen throw an error

command linescriptterminal

Goal: Cleaning up

Selectively truncate filenames in my nvALT notes folder. I tried using a productivity blogger's suggestion (which eludes me after a thorough search) to prepend ^ > - + to filenames, but it's not working for me. It's time to clean house! However, I need to make sure I don't lop off any alpha characters on filenames that do not use those prefixes. Plus I'm learning scripting and think it's fun!

Shell script

Here's what I've got:

#!/bin/bash

cd ~/dropbox/notes_test

for f in *; do
  FILENAME=$(basename "$f")
  DIRNAME=$(dirname "$f")
  if [[ "$f" == \^\ * ]]; then
    mv "$f" "${DIRNAME}/${FILENAME:2}"
  fi
  if [[ "$f" == \+\ * ]]; then
    mv "$f" "${DIRNAME}/${FILENAME:2}"
  fi
  if [[ "$f" == \>\ * ]]; then
    mv "$f" "${DIRNAME}/${FILENAME:2}"
  fi
  if [[ "$f" == \-\ * ]]; then
    mv "$f" "${DIRNAME}/${FILENAME:2}"
  fi
done

Problem: The hyphen

Here's the command line input and error message. I get one of these for each filename beginning with "- ".

DEV0041:scripts n$ ./truncate.sh
basename: illegal option --  
usage: basename string [suffix]
       basename [-a] [-s suffix] string [...]
dirname: illegal option --  
usage: dirname path
mv: illegal option --  
usage: mv [-f | -i | -n] [-v] source target
       mv [-f | -i | -n] [-v] source ... directory

Question: Why does the hyphen—and only the hyphen—throw an error?

And how to work around this issue?

Best Answer

From the man page for mv, the first (and possibly second) argument on the command line, if it begins with a -, is an option and not a file. Only -f | -i | -n are allowed options.

Simplest way is not to use - in a file name—it will confuse other command line programs—given that prepending - is a workaround for something else I would just not use that character.

If this is not an option, you can rewrite the mv commands like this

mv ./"$f" "${DIRNAME}/${FILENAME:2}"

or (a bit more generic because it then also works for absolute paths)

mv -- "$f" "${DIRNAME}/${FILENAME:2}"

Another option (and is what I would do for any bash script longer than a few lines) is write in a scripting language like perl or python - in this case they solve the problem by their move functions not passing the filenames to mv