首页 > 解决方案 > 从数组构建选项时,如何在查找命令中使用括号“()”?

问题描述

我有一个看起来像这样的功能。我已经剥离了错误处理,函数之外的命令是为了确保我在示例中找到了一些东西。

#!/bin/bash

findfiles() {
  local path=$1
  local mtime=$2
  local prunedirs=$3

  local -a fopts
  fopts+=("$path")

  [[ -n $prunedirs ]] && {
    fopts+=('-type' 'd')
    fopts+=('(' '-path')
    fopts+=("${prunedirs// / -o -path }")
    fopts+=(')' '-prune' '-o')
  }

  fopts+=('-type' 'f')
  fopts+=('-writable')
  fopts+=('-mtime' "+$mtime")

  [[ -n $prunedirs ]] && fopts+=('-print')

  echo "find ${fopts[*]}"
  find "${fopts[@]}"
}

mkdir -p dir1/{dir2,dir3}
touch dir1/5daysago.txt -mt "$(date -d 'now - 5 days' +%Y%m%d%H%M)"
touch dir1/dir2/6daysago.txt -mt "$(date -d 'now - 6 days' +%Y%m%d%H%M)"
touch dir1/dir3/10daysago.txt -mt "$(date -d 'now - 10 days' +%Y%m%d%H%M)"

echo '---------------------------------------------'
findfiles dir1 4
echo '---------------------------------------------'
findfiles dir1 4 'dir1/dir2'
echo '---------------------------------------------'
findfiles dir1 4 "dir1/dir2 dir1/dir3"

这将输出以下内容:

---------------------------------------------
find dir1 -type f -writable -mtime +4
dir1/dir2/6daysago.txt
dir1/dir3/10daysago.txt
dir1/5daysago.txt
---------------------------------------------
find dir1 -type d ( -path dir1/dir2 ) -prune -o -type f -writable -mtime +4 -print
dir1/dir3/10daysago.txt
dir1/5daysago.txt
---------------------------------------------
find dir1 -type d ( -path dir1/dir2 -o -path dir1/dir3 ) -prune -o -type f -writable -mtime +4 -print
dir1/dir2/6daysago.txt
dir1/dir3/10daysago.txt
dir1/5daysago.txt

请注意,第三次尝试不会修剪目录。如果我复制并粘贴查找(转义括号)它可以正常工作。

$ find dir1 -type d \( -path dir1/dir2 -o -path dir1/dir3 \) -prune -o -type f -writable -mtime +4 -print
dir1/5daysago.txt

我究竟做错了什么?

标签: bashfind

解决方案


更改echo "find ${fopts[*]}"declare -p fopts明确打印选项。这样做将表明该-o -path部分被添加为一个单词:

$ declare -p fopts
declare -a fopts=(
    [0]="dir1" [1]="-type" [2]="d" [3]="(" [4]="-path"
    [5]="dir1/dir2 -o -path dir1/dir3" [6]=")" [7]="-prune" [8]="-o" [9]="-type" [10]="f" 
#   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    [11]="-writable" [12]="-mtime" [13]="+4" [14]="-print"
)

要修复它,您需要将每个目录添加到单独修剪到数组中,例如:

local prunedirs=("${@:3}")

...

fopts+=(-type d '(' -false)
for dir in "${prunedirs[@]}"; do
  fopts+=(-o -path "$dir")
done
fopts+=(')' -prune -o)

我已经切换prunedirs到一个数组,所以它可以处理带有空格的目录名。

它从初始-false检查开始,因此无需检查是否prunedirs为空。如果它是空的,则仍然会添加整个内容,但因为它只是说它-type d '(' -false ')' -prune -o是无操作的。

另外,请注意您不必引用每个参数。可以编写-type d并且不加引号,就像在命令行中键入它们一样。只有'('并且')'需要单引号。


推荐阅读