无法在 bash 脚本中 cd 到(传递的)变量

无法在 bash 脚本中 cd 到(传递的)变量

我正在使用 bash 将一些信息从 audacious 传输到 conky 进行显示(包括专辑封面)。我从 audacious 获取音乐文件目录的路径,然后尝试 cd 到该目录以查找 folder.jpg - 如果找到,则准备显示。不幸的是,不能依赖“格式正确”的路径名。它们都没有从终端出现问题,但是 bash 的评估...它会因双倍空格()'/ 和其他可能的情况而阻塞,尽管当前设置似乎可以处理 - 没问题。以下是相关功能:

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
}

有什么想法吗,或者取出钢丝刷并清除我的“C”编译器上的锈迹会更容易吗?

我尝试使用双引号、单引号、反引号,甚至类似这样的构造:

code=$code \"\$filename\""

但目前似乎什么都不能正常工作。幸运的是,它失败了,因为它只是弹出“找不到艺术”替代图片,但有时事情会在 stderr 上出现,直到下一首歌曲或专辑。

答案1

您可以使用file_path="${file_path/#~/$HOME}"扩展只是~

据我所知,波浪号扩展是 输出中唯一需要执行的扩展audtool --current-song-tuple-data file-path。此外,波浪符号只出现过一种特殊情况:如果 Audacious 使用的路径以运行 Audacious 的用户的主目录开头,则路径的这一部分将~在 输出中替换为audtool。由于audaciousaudtool手册页没有阐明这一点,我尝试过让audtool输出路径以~username波浪号扩展的形式作为前缀,幸运的是,它似乎从来没有这样做过。我说“幸运”是因为这意味着情况非常简单。

由于该命令的输出需要进行的唯一转换audtool是将前导字符替换~为用户主目录的路径,因此您只需在脚本中编写代码来执行该扩展本身,而其他情况下则不修改路径。您发现您可能播放的音频文件的文件名可能包含 shell 特殊处理的字符,并且如果你播放其他人命名的文件甚至可能导致安全漏洞在你的脚本中通过不让 shell 执行(甚至扩展)任意、不受信任的文本,您可以完全避免该问题。

有几种方法可以扩展~自己。我建议:

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

这将手动执行可能需要的一次转换。具体来说,当 的输出audtool以 开头时~,它将替换为用户的主目录(通过检查HOME变量的值获得)。当输出不以 开头时~,不会执行任何扩展。

${file_path/#~/$HOME}请注意,这种方法不是如果将其用作模拟所有形式的波浪号扩展的尝试,则是正确的,因为它会以该~username形式(以及其他一些更晦涩的形式)执行不正确的替换。但是,只要输出中的路径audtool仅在指定当前用户的主目录的简单情况下使用波浪号符号(我相信是这种情况),那么这种方法适合并且正确这些路径。

其工作方式如下:

  • 一般来说,Bash 将扩展 ${parameter/pattern/string}的值parameter,但匹配的部分pattern替换为string。如果没有部分与模式匹配,则parameter使用 的精确值。
  • pattern以 开头时#,它只能从 的值的最开头开始匹配parameter。(除此之外,还有其他字符#在这个位置有意义: a%要求模式在 的最开头进行匹配结尾,并且 a/会导致模式被匹配和替换多次,而不是最多一次。)
  • ~在模式中没有特殊含义,因此按字面意思处理,作为要匹配和替换的字符。

参数扩展了解详情。

请注意,我编写的代码不会处理audtool产生空输出的情况,例如当没有歌曲正在播放时(或者,Simon Sudler 提到,如果有错误)。但是,它不会因输出为空而中断,也不会妨碍后续处理。因此,如果您想解决这种情况,您仍然可以。

最后,我应该提到,我忽略了两个我认为对您的用例不重要的区别:

  1. 由于audtool的路径来自 Audacious,如果您尝试以一个用户的身份运行 Audacious 并audtool以另一个用户的身份运行这种奇怪的情况 - 并且您以某种方式配置了 D-Bus 以便可以工作 - 那么您必须关心~引用与运行脚本的用户不同的用户的主目录。
  2. 从技术上讲,当前用户的波浪符号扩展(在带有 的 shell 中~,或者紧接着带有 的shell 中/)不是相当相当于"$HOME"。 Shell 可能会尝试处理未设置的奇怪情况HOME。 Bash 会尝试处理这种情况并仍然产生正确的扩展。 不过,我怀疑您是否关心此目的的区别。 请参阅波浪号扩展了解详情。

答案2

file_path无需重新分配变量。有两种情况可能会导致脚本中断:

  1. 该变量为空,因为audtool返回错误
  2. 返回audtools一个包含空格的目录。
  3. audtool包括~主目录

以下是关于如何解决这些问题的建议:

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
    fi
    folder_jpg="$file_path/folder.jpg"
    if [[ ! -e "$folder_jpg" ]]; then
        cp ~/Work/vinyl.png /tmp/cover.png
    else
        convert "$folder_jpg" -resize 120x120 /tmp/cover.png
    fi
}

处理"$(command)"文件夹名称中的空格(如果有)。cd如果不需要,请不要在脚本中使用。检查返回字符串是否是目录始终是一个好主意。在大多数情况下,使用绝对路径更好,并且可以防止在错误的文件夹中执行命令。

~bash 中扩展的注释:

的扩展~由 bash 处理,因此readlink -f在这里无济于事。因此,包含 的变量~必须以非引号形式执行才能扩展(我更新了脚本)。这当然可以在一行中处理...

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

# expansion will not work, with quoted ~
$ set -x; readlink -f '~'; set +x
+ readlink -f '~'
/home/user/~
+ 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
/home/user
+ set +x

相关内容