首页 > 解决方案 > 命令显示正确,但执行方式不同

问题描述

我有以下代码用他们的艺术家标记我的 FLAC 文件:

#!/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

这会输出命令(带有 echo "metaflac ..." 部分),当通过复制和粘贴执行时可以完美执行,但是在 echo 之后的命令会输出“找不到文件”-错误。我错过了什么?

编辑:引用文件名也会给出文件未找到错误,然后执行单词分割。

ls "some file with spaces"
"some : File not found!
file : File not found!
with : File not found!
spaces" : File not found!

标签: linuxbashscripting

解决方案


乍一看,我认为您的问题是实际命令中不必要的引号转义,例如--set-tag=\"ARTIST=$artist\". 但是,您得到的错误是该文件不存在。

这是因为您正在修改文件名,然后只是希望该文件存在。但是您实际上并没有重命名该文件。您所做的只是修改了变量fname,其中包含实际文件名的修改版本,该文件名存储在file.

目前尚不清楚您转义 中的空格和括号的意图是什么$fname,但无论如何,该文件实际上并不存在。

我怀疑您正在尝试转义括号和空格,以便外壳不会抱怨,就像您在命令行上直接输入文件名时所做的那样,例如:

ls  some\ file\ \(with\ spaces\ and\ parentheses\).txt

但是,在您的脚本中,您不需要经历所有这些麻烦。您只需要确保$file在命令中使用它时引用它:

metaflac --set-tag="ARTIST=$artist" "$file"

当您在文件名周围加上引号时,shell 不会尝试对文件名进行分词。此外,尝试逃避像你正在做的事情实际上并不会奏效,因为 shell 在扩展后不会识别你的逃避$fname。相反,它会将它们视为文字\字符。

例如:

> 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

即使您删除 周围的引号$var,它仍然不起作用:

> ls $var
ls: cannot access 'some\': No such file or directory
ls: cannot access 'file': No such file or directory

删除引号会导致 shell 对扩展变量进行分词,但它仍然将反斜杠视为文字字符而不是转义符。您可以使用它来使其工作eval(但这仍然不是正确的方法):

> eval "ls -l $var"
-rw-r--r-- 1 user user 0 2019.02.01 12:11 some file

正确的方法是确保正确引用变量,而不是试图逃避:

> 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

您也可以直接在命令行上使用引号,以避免转义:

> 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

推荐阅读