抱歉,我是 shellscripting 新手。我有一个变量,其中包含与命令相关的选项find
。
TYPE=("-type f")
NAME=("-name \"*log*\"")
我尝试将那些包含选项的变量放入相关代码中。
- 这有效:
并产生(样本输出):LST_FILE=$(find ${2} ${TYPE} -mmin +${HOUR_TO_MIN})
viv/clean/prototype/asdadsadsa.log viv/clean/prototype/logo viv/clean/prototype/sadsadasdas.log.gz viv/clean/prototype/prototype/log viv/clean/prototype/prototype/fewfasfs viv/clean/prototype/prototype/og
- 但是当我尝试添加它
${NAME}
无法捕获关键字名称来搜索我想要的文件时,它没有结果。
(产生 0 个结果)LST_FILE=$(find ${2} ${TYPE} ${NAME} -mmin +${HOUR_TO_MIN})
似乎问题与通配符、特殊字符或类似的东西有关。非常感谢您对相关案例的建议。
答案1
这里有两个问题。第一个问题是您将两个变量定义为只有一个元素的数组,然后在命令中将它们用作find
简单的标量变量。当您执行此操作时,bash 会礼貌地返回每个数组的第一个元素。
第二个,正如 C.Aknesil 指出的,bash 在扩展变量后不会删除双引号。事实上,这实际上并不是一个问题,问题是你不知道它或者为什么 bash 会这样表现——“这是一个特性,而不是一个错误”。
试试这样:
TYPE=(-type f)
NAME=(-name '*log*')
'*log*'
需要在这里加引号,以便 bash 不会扩展 glob 并将当前目录中的所有匹配文件添加到数组中。
然后,当您需要将它们与以下内容一起使用时find
:
find "$2" "${TYPE[@]}" -mmin "+$HOUR_TO_MIN"
或者
find "$2" "${TYPE[@]}" "${NAME[@]}" -mmin "+$HOUR_TO_MIN"
顺便说一句,请注意,这里$2
和都$HOUR_TO_MIN
应该用双引号引起来,但{}
不需要大括号,因为不需要将它们与周围的字符消除歧义。例如$2
不需要{}
,但${2}3
需要它们,因为它会被解释为$23
(和不是如“$2 后跟 3”)由没有它们的 shell 执行。
$HOUR_TO_MIN
不引用可能是可以的(因为它可能是在脚本中定义的,并且可能没有像空格等问题字符),但引用它并没有什么坏处,并且“总是引用你的变量(除非你知道绝对确定您不想这样做,并且为什么)”是一个值得养成的好习惯。
$2
然而,由用户作为脚本的参数提供,并且其中可以包含任何内容,并且应该始终被引用。
注意:这样做并不是一个好主意,LST_FILES=$(find ...)
因为文件名中可能包含各种烦人的字符,从空格、制表符和换行符等空白到;
、&
、>
和 等shell 元字符|
。唯一的角色是不能文件名中是 NUL,这意味着 NUL 是唯一可靠的文件名分隔符任何文件名。
相反,如果您需要在变量中查找 find 的输出,请使用数组并告诉find
使用 NUL 作为带有-print0
.例如
mapfile -d '' LST_FILES < <(find "$2" "${TYPE[@]}" "${NAME[@]}" -mmin "+$HOUR_TO_MIN" -print0)
该mapfile
命令(也称为readarray
)从 stdin 读取并使用它来填充数组。在本例中,我们指示使用 NUL 作为 的分隔符-d ''
。
尝试执行以下操作,看看它对您自己有何效果:
$ mkdir junk
$ cd junk
$ touch foo bar baz 'file with spaces' $'file\nwith\nLFs'
$ ls
bar baz file?with?LFs file with spaces foo
$ mapfile -d '' LST_FILES < <(find . -type f -print0)
$ declare -p LST_FILES
declare -a LST_FILES=([0]="./foo" [1]=$'./file\nwith\nLFs' [2]="./baz"
[3]="./file with spaces" [4]="./bar")
$ echo "${#LST_FILES[@]}" # use ${#...} to count elements in an array
5
$ for f in "${LST_FILES[@]}"; do echo "$f" ; done
./foo
./file
with
LFs
./baz
./file with spaces
./bar
答案2
我的结论是 Bash 不会在分词后对单个单词执行引号删除。因此,您提供的模式"*log*"
中包含双引号。换句话说,您正在搜索名称以双引号开头和结尾的文件。
来自bash手册:
展开的顺序是:大括号展开;波形符扩展、参数和变量扩展、算术扩展和命令替换(以从左到右的方式完成);分词;和文件名扩展。 [...] 执行这些扩展后,原始单词中存在的引号字符将被删除,除非它们本身已被引用(引号删除)。
您可以使用以下代码观察此行为:
$ NAME=("-name \"*log*\"")
$ echo $NAME
-name "*log*"
正如您所看到的,$NAME
提供给 的echo
方式与提供给 的方式相同find
。echo
打印双引号的事实表明,在调用 之前,模式周围的引号不会被删除echo
。
作为修复,我建议使用find
不带变量的模式。要实现类似的代码分解,您可以创建一个使用 的函数find
,并在后面调用该函数:
function find_files {
find ... <pattern>
}
LST_FILE=$(find_files)
我找不到可以将模式存储到变量中的解决方案。我希望上面的解决方案足够了。