我需要将存储在变量中的字符串传递给查找命令字符串。
我做什么:
s="\( ! -path \"/path/to/1/Recycle Bin\" \) -or"
s="${s} \( ! -path \"/path/to/2/Recycle Bin\" \)"
exec=$(find "/path/to/Recycle Bin" -type d $s)
结果:
find: paths must precede expression: `\('
如果我做:
exec=$(find "/path/to/Recycle Bin" -type d \( ! -path "/path/to/1/Recycle Bin" \) -or \( ! -path "/path/to/2/Recycle Bin" \))
有用。
我究竟做错了什么?
答案1
我认为,在 bash 中执行此操作的唯一可靠方法是使用数组。
正确引用的数组允许您构建一个由可能包含空格的单独可解析的参数组成的命令行。
前任。
$ a=( find "/path/to/Recycle Bin" -type d )
$ a+=( \( ! -path "/path/to/1/Recycle Bin" )
$ a+=( -or ! -path "/path/to/2/Recycle Bin" \) )
我们可以通过打印出各个元素(每行一个)来查看 shell 将看到的内容:
$ printf '%s\n' "${a[@]}"
find
/path/to/Recycle Bin
-type
d
(
!
-path
/path/to/1/Recycle Bin
-or
!
-path
/path/to/2/Recycle Bin
)
答案2
如果你的变量可能不包含你认为的内容,请使用
set -x
查看每次扩展的结果。
shell 处理find
命令中的反斜杠和双引号,但是在扩展变量时它不会以相同的方式处理它们......
在命令行中我们写道:
find "/path/to/Recycle Bin" -type d \( ! -path "/path/to/1/Recycle Bin" \) -or \( ! -path "/path/to/2/Recycle Bin" \)
如果你已经运行了set -x
,你可以看到 Bash 对此做了什么以及实际传递给了什么find
:
+ find '/path/to/Recycle Bin' -type d '(' '!' -path '/path/to/1/Recycle Bin' ')' -or '(' '!' -path '/path/to/2/Recycle Bin' ')'
变量上的引号删除不会发生(你把\
和"
字符放在那里,所以你想保留它们,对吗?这些字符是“扩展的结果”,所以它们是未被移除)(当您按预期分配它时\"
变为,因为在双引号中反斜杠引用了字符),但 wordsplitting 确实如此:"
\$`"
$ find "/path/to/Recycle Bin" -type d $s
+ find '/path/to/Recycle Bin' -type d '\(' '!' -path '"/path/to/1/Recycle' 'Bin"' '\)' -or '\(' '!' -path '"/path/to/2/Recycle' 'Bin"' '\)'
find: paths must precede expression: \(
引用变量可以修复错误的单词拆分,但也可以防止标记化:
$ find "/path/to/Recycle Bin" -type d "$s"
+ find '/path/to/Recycle Bin' -type d '\( ! -path "/path/to/1/Recycle Bin" \) -or \( ! -path "/path/to/2/Recycle Bin" \)'
find: paths must precede expression: \( ! -path "/path/to/1/Recycle Bin" \) -or \( ! -path "/path/to/2/Recycle Bin" \)
几乎你对这两个字符串所做的任何操作都会导致find
看到错误的内容。让你的变量只包含一个扩展为的字符串'(' '!' -path '/path/to/1/Recycle Bin' ')' -or '(' '!' -path '/path/to/2/Recycle Bin' ')'
是一场我个人无法赢得的引号之战。
这样就可以了
s="/path/to/1/Recycle Bin"
t="/path/to/2/Recycle Bin"
find "/path/to/Recycle Bin" -type d \( ! -path "$s" \) -or \( ! -path "$t" \)
如果您不使用带空格的路径来消除引用的需要,那么您也可以摆脱它 - 如果您去掉反斜杠和双引号:
s="( ! -path /path/to/1/Trash ) -or"
s="${s} ( ! -path /path/to/2/Trash )"
find /path/to/Trash -type d $s
也许如果你解释为什么要这样做,有人就可以建议更好的解决方法。