7

I am testing in Bash for if a file is existing, where the file name is escaped using $(printf '%q' "$FNAME").

This always produces an error using if [ -f $FNAME ] as in the commented example below. How can I test for a filename that contains spaces and other characters?

#!/usr/bin/env bash

# code used in Raspberry Pi Podcasting Jukebox project
# youtube-dl -f 17 --get-filename https://www.youtube.com/watch?v=AgkM5g_Ob-w
# returns "HOW ABUNDANCE WILL CHANGE THE WORLD - Elon Musk 2017-AgkM5g_Ob-w.3gp"

# Purpose: To test if file exists before downloading
# for testing purposes using an existing regular file "abc def ghi"
AFILE="abc def ghi"
TFILE=$(printf '%q' "$AFILE") # Escaping filename using printf
echo $TFILE # returns abc\ def\ ghi
# if [ -f $AFILE ] # this test returns false every time with error [:too many arguments

if [ -f $TFILE ] # This test also returns FALSE with err [: too many arguments
then
  echo "Existing"
  # don't download
else
  echo "Not existing"
  # youtube-dl http://www.youtube.com/watch?v=AgkM5g_Ob-w
fi
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
casperl
  • 79
  • 1
  • 3
  • Possible duplicate of [Bash and filenames with spaces](https://stackoverflow.com/questions/1574898/bash-and-filenames-with-spaces) – Jean-Baptiste Yunès Dec 13 '17 at 08:47
  • Yes, a similar question, but [Bash and filenames with spaces](https://stackoverflow.com/questions/1574898/bash-and-filenames-with-spaces) does not contain the solution to the specific problem of making the testing conditon an expression in Bash through [[ ]] – casperl Dec 13 '17 at 09:02

1 Answers1

10

Always quote your file-name, the idea of using %q for escaping the spaces is right, but when used with the [ operator the unquoted $TFILE is split into multiple words causing the -f operand to receive too many arguments when it was actually expecting a single argument. So once you double-quote it, the white-spaces are preserved and a literal single argument is passed in the conditional.

testFile="abc def ghi"
printf -v quotedFile '%q' "$testFile"

if [ -f "$quotedFile" ]; then
    printf 'My quoted file %s exists\n' "$quotedFile"
fi

the above should apply well (the usage of [) in any POSIX compatible shells. But if you are targeting scripts for bash shell alone, you can use the [[ in which quoting is never necessary as it evaluated as an expression. So you can just do

file_with_spaces="abc def ghi"
if [[ -f $file_with_spaces ]]; then
    printf 'My quoted file %s exists\n' "$file_with_spaces"
fi

But in general it doesn't hurt to add quotes to variables in bash. You can always do it.

Inian
  • 80,270
  • 14
  • 142
  • 161
  • 1
    A very detailed explanation, I wIll quote Bash variables in future. Since the target platform is a Raspberry Pi using Raspbian/Wheezy I am limited to Bash and your additional Bash example works perfectly. – casperl Dec 13 '17 at 09:08
  • Your first example with `-f` can't work because `"$quotedFile"` contains backslashes, unlike the filename. `if [ -f "$testFile" ];` would work fine, just like the `[[` case. It's not that `[[` "evaluates as an expression" exactly; the difference with `[` is that it doesn't word-split. (Quoting man bash: "Word splitting and pathname expansion are not performed on the words between the `[[` and `]]`; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and quote removal are performed.") – rici Oct 12 '19 at 01:46
  • (Sorry for responding after almost two years. A stray edit brought the question back into my list, and I'd responded before I saw the date.) – rici Oct 12 '19 at 01:48