在 bash 和 qmake 中转义变量的双引号

在 bash 和 qmake 中转义变量的双引号

我为 qmake 编写了以下元项目文件,旨在构建.pro以下子目录中的所有文件(由于历史原因和其他工具链,文件名与文件夹名称不匹配)。本质上可以归结为这样的事情(抛开项目特定的东西)

THIS_FILE=make-all.pro
TEMPLATE=subdirs
FIND= "find -name \'*.pro\' -printf \'%p\\n\'"
AWK= "awk \'{$1=substr($1,3); print}\'"
RMTHIS= "awk \'!/$$THIS_FILE/\'"
SUBDIRS= $$system($$FIND | $$AWK | $$RMTHIS )

.pro我需要获取包含Bash 脚本中的文件的文件夹列表,因此我决定复制该方法

#!/bin/bash
FIND="find -name '*.pro' -printf '%h\\n"
AWK="awk '{\$1=substr(\$1,3);printf}'"
SUBDIRS=$($FIND | $AWK)

显然这不起作用,表达式中awk出现错误。invalid char ''尝试直接在 Bash 中执行相同的行表明,awk只有使用双引号才真正有效

find -name '*.pro' | awk "{\$1=substr(\$1,3);printf}"

将有问题的行替换为

AWK='awk "{$1=substr($1,3);printf}" '

没有给出任何工作结果,脚本的输出是空的,与手动输入命令的输出不同。显然

 find -name '*.pro' 

在脚本中仅查找当前文件夹中的文件,而在 bash 命令行中的对应项则在子文件夹中查找它。出了什么问题以及为什么 qmake 的工作方式也不同?

答案1

我不熟悉 qmake 语法,但从您的示例来看,它使用引号和变量的方式与 shell 非常不同。所以你不能只使用相同的代码。

http://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-characters涵盖了您需要了解的内容,所以在这里我将总结与您的问题相关的内容。

简而言之,您不能简单地将 shell 命令填充到字符串中。 bash 等 shell 不会递归地解析字符串。他们解析源代码并构建命令为列表字符串:一个简单的命令由命令名称(可执行文件的路径、函数名称等)及其参数组成。

当您使用诸如 之类的赋值时AWK="awk '{\$1=substr(\$1,3);printf}'",会将变量设置AWK为字符串;当您将变量用作$AWK外部双引号时,这会通过在空格处解析变量的值将其转换为字符串列表,但不会将其解析为 shell 代码,因此像这样的字符'最终会真正出现在参数中。这是很少需要的,这就是为什么在 shell 中使用变量的一般建议是在变量扩展周围加上双引号除非你知道你需要放弃它们并且你明白这意味着什么。 (请注意,我在这里的回答并没有讲述整个故事。)

在 bash 中,您可以将简单的命令填充到数组中。

#!/bin/bash
FIND=(find -name '*.pro' -printf '%h\\n')
AWK=(awk '{$1=substr($1,3);print}')
SUBDIRS=$("${FIND[@]}" | "${AWK[@]}") 

但通常存储命令的最佳方式是定义一个函数。这不限于简单的命令:这样您可以进行重定向、变量赋值、条件等。

#!/bin/bash
find_pro_files () {
  find -name '*.pro' -printf '%h\\n'
}
truncate_names () {
  awk '{$1=substr($1,3);print}'
}
SUBDIRS=$(find_pro_files | truncate_names)

我不确定您想用这个脚本做什么(特别是考虑到您在代码片段之间更改了findawk代码),但可能有更好的方法来做到这一点。您可以使用循环遍历*.pro当前目录的子目录中的文件

for pro in */*.pro; do
  subdir="${pro%/*}"
  basename="${pro##*/}"
done

如果你想递归地遍历子目录,在 bash 中,你可以使用**/*.pro代替*/*.pro,但要注意,这也会递归到目录的符号链接。

答案2

如果您需要做的只是查找直接包含带有.pro扩展名的文件的目录,则可以使用 xargs 和可选的 uniq,如果您只想列出一个目录一次,即使它包含多个 pro 文件:

find -type f -name "*.pro" | xargs -I{} dirname {} | uniq

如果您需要绝对路径而不是相对路径:

find -type f -name "*.pro" | xargs -I{} dirname {} | xargs readlink -f | uniq

根据您的要求,使用 awk 的解决方案:

find -type f -name "*.pro" | awk -F/ 'BEGIN { OFS="/" } { $1=""; $NF=""; print $0 }' | cut -c 2- | uniq

这正是您所需要的;不带前缀的相对路径./

相关内容