I am using bash to get some information from audacious to conky for display (including album art). I get the path to the directory of the music file from audacious, then attempt to cd to that directory to find folder.jpg - if found, prepare for display. Unfortunately can't rely on 'well-formed' path names. None of them have a problem from terminal, but the evaluations that bash does... It chokes on doubled spaces ( ) ' / and probably others, although the current setup seems to handle - ok. Here's the relevant function:
GetArt ()
{
file_path=`audtool --current-song-tuple-data file-path` # get the path to the song
file_path=$(eval echo "${file_path}") # pre-expand to full path
cd "${file_path}"
if [[ ! -e "folder.jpg" ]]; # if no art work found
then
cp ~/Work/vinyl.png /tmp/cover.png # put in placeholder
else
convert "${file_path}""/folder.jpg" -resize 120x120 /tmp/cover.png # ready for showing
fi
}
Any ideas, or would getting out the wire brush and removing the rust from my 'C' compiler be easier?
I tried with double quotes, single quotes, backticks, and even a construct like:
code=$code \"\$filename\""
but nothing seems to work correctly as yet. Fortunately it fails 'pretty' because it just pops up the "can't find art" substitute pic instead, but sometimes things burp all over stderr until the next song - or album.
You can use
file_path="${file_path/#~/$HOME}"
to expand just the~
.As far as I can tell, tilde expansion is the only kind of expansion that needs to be performed on the output of
audtool --current-song-tuple-data file-path
. Furthermore, only one specific case of tilde notation ever appears: if the path Audacious is using begins with the home directory of the user running Audacious, that part of the path is replaced with a~
in the output ofaudtool
. Since theaudacious
andaudtool
manpages don't clarify this, I've tried to getaudtool
to output paths prefixed with the~username
form of tilde expansion, and fortunately it appears never to do even that. I say "fortunately" because this means the situation is pretty simple.Since the only transformation that needs to be done on the output of that
audtool
command is to replace a leading~
with the path of the user's home directory, you can simply write code in your script that does that one expansion itself, and otherwise leaves the path unmodified. You've found that filenames of audio files you may play can contain characters treated specially by the shell, and if you play files named by other people it may even give rise to a security vulnerability in your script. By not having the shell execute--or even expand--arbitrary, untrusted text, you avoid that problem entirely.There are several ways to expand
~
yourself. I suggest:This manually performs the one transformation that may be needed. Specifically, when the output of
audtool
starts with a~
, that replaces it with the user's home directory, obtained by checking the value of theHOME
variable. When the output doesn't start with a~
, no expansion is performed.Note that this approach,
${file_path/#~/$HOME}
, would not be correct if it were used as an attempt to simulate all forms of tilde expansion, because it would perform incorrect substitutions in the~username
form (and in some of the other, more obscure forms). However, so long as paths in the output ofaudtool
only use tilde notation in the simple case of designating the current user's home directory--which I believe to be the case--then this approach is appropriate and correct for those paths.The way it works is:
${parameter/pattern/string}
into the value ofparameter
, but with the part that matchespattern
replaced withstring
. If no part matches the pattern, the exact value ofparameter
is used.pattern
is written with a leading#
, it can only be matched starting at the very beginning of the value ofparameter
. (There are other characters besides#
that are meaningful in this position: a%
would require the pattern to be matched at the very end, and a/
would cause the pattern to be matched and replaced as many times as it appears rather than at most once.)~
has no special meaning in a pattern and is therefore treated literally, as a character to match and replace.See parameter expansion for details.
Note that the code I have written doesn't do anything to handle the case of
audtool
producing empty output, as happens when there is no song currently playing (or, as Simon Sudler mentioned, if there is an error). However, it doesn't break on empty output or get in the way of subsequent handling of it. So if you want to cover that case, you still can.Finally, I should mention that I've glossed over two distinctions that I believe are unimportant to your use case:
audtool
's paths come from Audacious, if you were attempting the bizarre situation of running Audacious as one user and runningaudtool
as another--and you somehow configured D-Bus so that worked--then you would have to care about~
referring to a different user's home directory than the user running the script.~
by itself or followed immediately by/
) is not quite equivalent to"$HOME"
. Shells may attempt to handle the strange case whereHOME
is not set. Bash does attempt to handle this and still produce a correct expansion. I doubt you care about the distinction for this purpose, though. See tilde expansion for details.The re-assignment of the
file_path
variables is not necessary. There are 2 signs that can happen, that might break the script:audtool
return an erroraudtools
returns a directory, which contains white-spaces.audtool
includes~
for the home directoryHere a proposal on howto address these issues:
The
"$(command)"
handles the white-spaces in the folder name (if there are any). Don't usecd
inside a script, if you don't need it. Checking if the return string is a directory is always a good idea. Working with the absolute path is in most cases better and prevents the execution of commands in the wrong folder.Notes to the
~
expansion in bash:The expansion of the
~
is handled by the bash, soreadlink -f
does not help here. So the variable containing the~
must be executed un-quoted to be expanded (I updated the script). This can of course be handled in a single line...