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
        cp ~/Work/vinyl.png /tmp/cover.png              # put in placeholder
        convert "${file_path}""/folder.jpg" -resize 120x120 /tmp/cover.png # ready for showing

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 attribute file_path twice I guess the first line is useless? and the you eval it, eval is evil don't use it also why? why can't you just pass what the user passed as argument? – tatsu Jun 12 at 8:20

The re-assignment of the file_path variables is not necessary. There are 2 signs that can happen, that might break the script:

  1. The variable is empty, because the audtool return an error
  2. The audtools returns a directory, which contains white-spaces.
  3. audtool includes ~ for the home directory

Here a proposal on howto address these issues:

GetArt ()
    file_path="$(audtool --current-song-tuple-data file-path)"
    file_path="$(readlink -f "$(bash -c "echo $file_path")")"
    if [[ ! -d $file_path ]]; then
        echo "error: $file_path does not exists"
        exit 1
    if [[ ! -e "$folder_jpg" ]]; then
        cp ~/Work/vinyl.png /tmp/cover.png
        convert "$folder_jpg" -resize 120x120 /tmp/cover.png

The "$(command)" handles the white-spaces in the folder name (if there are any). Don't use cd 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, so readlink -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...

# expansion from current bash
$ set -x; readlink -f ~; set +x
+ readlink -f /home/user
+ set +x

# expansion will not work, with quoted ~
$ set -x; readlink -f '~'; set +x
+ readlink -f '~'
+ set +x

# expansion from un-quoted ~ with sub-shell
$ set -x; readlink -f "$(bash -c "echo ~")"; set +x
++ bash -c 'echo ~'
+ readlink -f /home/user
+ set +x
  • 1
    audtool will often, but not always, output paths that need to undergo tilde expansion, such as ~/Albums/Dave Brubeck Quartet - Time Out. I'm pretty sure this is what motivated the OP to try to induce the shell to perform expansions in the path. Clearly most of the expansions (and splitting) that will happen with the techniques the OP attempted is not desired. But it seems to me that some processing of the output of audtool --current-song-tuple-data file-path is still needed. In [[, not even tilde expansion happens on the operand of -d . – Eliah Kagan Jun 12 at 8:26
  • Thanks for updating this answer... but readlink -f doesn't do what I think you want it to do. It doesn't expand ~. For example, cd /; readlink -f '~' outputs /~. The command readlink -f ~ does output a path in which the ~ has been expanded, but this is only because the shell itself performs tilde expansion on ~ before passing the result to readlink. – Eliah Kagan Jun 12 at 9:09
  • The readlink solution, if only providing expansion by where it is used, may be better for my use because the Music directory seen by Audacious is soft linked to /mnt/data2/Music. That would surely mean that the path would be more surely correct? I will try it out with some known problems. Both ideas avoid my use of cd - which appears to be the source of the trouble. Too much AmigaDOS experience, not enough Linux. – freebird54 Jun 12 at 9:30
  • With the update to the second (readlink) solution, it appears to work very well, including /mnt/data2/Music in the path output. The First answer also worked for resolving the ~ problem that led to the whole mess! Thanks to both - it even handles some of the ugly ones! Thanks to Simon and @Eliah both. My up votes don't count yet, but they are there. – freebird54 Jun 12 at 11:55

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 of audtool. Since the audacious and audtool manpages don't clarify this, I've tried to get audtool 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:

file_path="$(audtool --current-song-tuple-data file-path)"  # might need tilde expansion
file_path="${file_path/#~/$HOME}"  # do the tilde expansion, if needed

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 the HOME 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 of audtool 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:

  • In general, Bash will expand ${parameter/pattern/string} into the value of parameter, but with the part that matches pattern replaced with string. If no part matches the pattern, the exact value of parameter is used.
  • When pattern is written with a leading #, it can only be matched starting at the very beginning of the value of parameter. (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:

  1. Since audtool's paths come from Audacious, if you were attempting the bizarre situation of running Audacious as one user and running audtool 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.
  2. Technically speaking, tilde expansion for the current user (which happens in a shell with ~ by itself or followed immediately by /) is not quite equivalent to "$HOME". Shells may attempt to handle the strange case where HOME 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.