Reputation: 45
I have the following code to tag my FLAC files with their artist:
#!/bin/bash
# This script gets called with a directory of flac-audio-files to tag
#
# The files MUST have a filename of format "Artist - Title.flac"
# The whitespaces in the filename-format are optional
for file in $1/*; do
name=$(echo "$file" | cut -d'/' -f 2) # removes leading slashes and dirnames
artist=$(echo "$name"| cut -d'-' -f 1) # cuts the artists name out of the name
artist=$(echo "$artist" | awk '{$1=$1};1') # removes Whitespaces before and after
fname=$(echo "$file" | awk '{gsub(/ /,"\\ ");print}') # Supposed to escape spaces
fname=$(echo "$file" | awk '{gsub(/\(/,"\\(");print}') # Supposed to escape (
fname=$(echo "$file" | awk '{gsub(/)/,"\\)");print}') # Supposed to escape )
echo "metaflac --set-tag=\"ARTIST=$artist\" $fname"
metaflac --set-tag=\"ARTIST=$artist\" "$fname" # Tags the Song, finally
done
This outputs commands (with the echo "metaflac ..." portion), that execute flawlessly when executed via copy&paste, but the command that comes after that echo outputs a "file not found"-error. What did I miss?
Edit: Quoting the filename gives file-not-found errors too, and word-aplitting is performed then.
ls "some file with spaces"
"some : File not found!
file : File not found!
with : File not found!
spaces" : File not found!
Upvotes: 0
Views: 125
Reputation: 4602
At first glance, I thought your problem was the unnecessary escaping of the quotes in the actual command, e.g. --set-tag=\"ARTIST=$artist\"
. However, the error you're getting is that the file doesn't exist.
This is because you're modifying the filename and then just expecting that file to exist. But you haven't actually renamed the file. All you've done is modified the variable fname
, which contains a modified version of the actual filename, which is stored in file
.
It's not clear what your intent is with escaping the spaces and parentheses in $fname
, but whatever the case, that file doesn't actually exist.
I suspect that you're trying to escape parens and spaces so that the shell doesn't complain, the way you would do when typing in a filename directly on the command line, e.g.:
ls some\ file\ \(with\ spaces\ and\ parentheses\).txt
However, in your script, you don't need to go through all that trouble. You just need to make sure you quote $file
when you use it in a command:
metaflac --set-tag="ARTIST=$artist" "$file"
The shell will not attempt to do word-splitting on the filename when you put quotes around it. Also, attempting to escape things like you're doing wouldn't actually work anyway, because the shell won't recognize your escapes after expanding $fname
. It will instead see them as just literal \
characters.
For example:
> touch "some file"
> ls -l
-rw-r--r-- 1 user user 0 2019.02.01 12:11 some file
> var="some\ file"
> ls "$var"
ls: cannot access 'some\ file': No such file or directory
Even if you remove the quotes around $var
, it still doesn't work:
> ls $var
ls: cannot access 'some\': No such file or directory
ls: cannot access 'file': No such file or directory
Removing the quotes causes the shell to do word-splitting on the expanded variable, but it still sees the backslash as a literal character and not an escape. You can get it to work by using eval
(but this is still not the correct way to be doing this):
> eval "ls -l $var"
-rw-r--r-- 1 user user 0 2019.02.01 12:11 some file
The correct way is to just make sure to quote variables properly, instead of trying to escape things:
> var="some file" # No need to escape the space
> ls -l "$var" # Quotes around $var
-rw-r--r-- 1 user user 0 2019.02.01 12:11 some file
You can also use quotes directly on the command line, in order to avoid having to escape things:
> ls this\ is\ the\ hard\ way.txt
this is the hard way.txt
> ls "this is the easy way.txt"
this is the easy way.txt
Upvotes: 1