1

It is well known that it's a bad idea to do something of the kind <command> $FILENAME, since you can have a file whose name is for example -<option> and then instead of executing <command> with the file -<option> as an argument, <command> will be executed with the option -<option>.

Is there then a general safe way to accomplish this? One hypothesis would be to add -- before the filename, but I'm not sure if that is 100% safe, and there could be a command that doesn't have this option.

1 Answer 1

3

First of all, always enclose your variable between double quotes (there are exceptions to this rule, but you will easily recognize them when the moment comes). The risk that your filename contains space characters is equally high (and probably higher) than a filename that begins with a minus sign.

Your first command should thus be:

<command> "$FILENAME"

Next, the -<option> case. You sum up the issue quite well: using -- is OK but you cannot blindly add -- to your command as all commands do not support this syntax.

A perfectly safe way to protect against variables that contain a filename starting with a minus sign (or any other sensitive character) is to change the filename in order to prepend a path to it, either a relative path (./file) or an absolute path (/foo/bar/file). That way, the first char is harmless since it is either . or /.

This code will add a relative path to $filename unless it is already an absolute path:

[[ "$filename" != /* ]] && filename="./$filename"

Personally, in my shell scripts, I prefer to change file arguments to their canonical representation (full path):

filename="$(readlink -f -- "$filename")"

or:

filename="$(realpath -m -- "$filename")"

(if you wonder which among realpath and readlink you should use, see this excellent answer)

4
  • Any reason why you prefer to turn file arguments into their canonical representation, other than preference? Apr 10, 2021 at 12:06
  • 2
    Though note that transforming foo.txt to /path/to/foo.txt can make a difference, depending on what you do with the filename in the end. E.g. a symlink containing a full path is different from a relative one (matters if the link is moved or the directory renamed), and tar will store the path as given in the archive, making the extraction also create a path with that particular name. Also, that should be readlink -f -- "$filename" unless of course you both add the initial ./ and then run readlink -f.
    – ilkkachu
    Apr 10, 2021 at 12:17
  • @AnswerMyQuestion One reason that comes to mind is logs (and script output to terminal): my script logs include the full path and using that path leads immediately to the file (it it exists). If I see, or if someone reports me "file foo not found", the very first question I ask myself is "in what directory was my script running?" (no such question with "file /foo/bar not found")
    – xhienne
    Apr 10, 2021 at 12:23
  • @ilkkachu You are right. Of course I don't use readlink -f if I want the original symlinks to be preserved. Amending my answer to include -- to readlink, thank you.
    – xhienne
    Apr 10, 2021 at 12:25

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .