我知道引用参数有规则,但无论我尝试什么,我似乎都不知道如何使其工作。
我有一个函数,它接受单个文件名作为参数,然后使用该文件名构建命令行并运行命令行。当任何文件中没有空格时,我能够使其工作,但是当引入带有空格的文件名时,它就会中断。我尝试让它与空格一起工作的所有方法要么不起作用,要么导致它也无法在没有空格的文件上起作用。
这是一个不带空格的示例脚本:
#!/bin/bash
list_files() {
file="${1}"
cmd="cat ${file}"
echo "Running command: '${cmd}'"
${cmd}
}
TARGET_DIR="${1}"
while IFS= read -d $'\0' -r file; do
list_files "${file}"
done < <(find ${TARET_DIR} -type f -print 0)
我刚刚创建了一个目录结构如下:
./files/
|-- one # ==> Contains the text "files/one"
|-- two # ==> Contains the text "files/two"
|-- three and four # ==> Contains the text "files/three and four"
|-- five # ==> Contains the text "files/five"
当我在上面的目录上运行上面的脚本时,我得到以下输出:
bash-3.2$ ./test.bash ./files
Running Command: 'cat ./files/one'
files/one
Running Command: 'cat ./files/two'
files/two
Running Command: 'cat ./files/three and four'
cat: ./files/three: No such file or directory
cat: and: No such file or directory
cat: four: No such file or directory
Running Command: 'cat ./files/five'
files/five
我做错了什么?无论文件名是什么,我怎样才能使这样的脚本正常工作?
注意:对于这个例子,它很简单,首先不将命令存储在变量中,只需打印它,然后通过键入命令本身两次来调用它,但对于我的真实脚本,命令要复杂得多,我想如果将来需要更改,只需将其维护在一处。
答案1
代替:
cmd="cat ${file}"
printf '%s\n' "Running command: '${cmd}'"
${cmd}
要么传递一个固定命令以eval
将其解释为 shell 代码。
cmd='cat -- "$file"'
printf '%s\n' "Running command: '${cmd}'"
eval "$cmd"
这将输出Running command: cat -- "$file"
可能不是你想要的。
或者(bash
具体)使用正确printf %q
引用$file
,以便将其扩展传递给eval
:
printf -v cmd 'cat -- %q' "$file"
printf '%s\n' "Running command: $cmd"
eval "$cmd"
或者,由于这里是一个简单的命令,因此您可以将该简单命令的参数存储在数组中:
cmd=(cat -- "$file")
printf 'Running command: '
printf ' %q' "${cmd[@]}"
printf '\n'
"${cmd[@]}"
如果您知道$file
保证不包含类似字符的特定字符:
(请注意,:
尽管这是 Unix 上文件名中完全有效的字符),您可以这样做:
cmd="cat:--:$file"
IFS=: # split on :
set -f # disable glob
# use the split+glob operator by leaving the variable unquoted:
printf 'Running command: '
printf ' %q' $cmd
printf '\n'
$cmd
现在,在这种特定情况下,您还可以执行以下操作:
(PS4='Running command: '
set -x; cat -- "$file")
(注意消息会发送到 stderr)。
甚至:
PS4='Running command: ' find "$TARGET_DIR" -type f -exec sh -xc '
cat "$1"' sh {} \;
或者为了避免运行那么多的sh
andcat
命令,请使用ksh93
wherecat
是映射到 /opt/ast/bin/cat 的内置函数,并使用{} +
语法来运行尽可能少的 shell:
PS4='Running command: ' find "$TARGET_DIR" -type f -exec ksh93 -c '
PATH=/opt/ast/bin; set -x; for file do cat "$file"; done' ksh93 {} +
要显示带有标题的文件内容并运行尽可能少的命令,在 GNU 系统上,您还可以使用head
:
$ find "$TARGET_DIR" -type f -exec head -vc-0 {} +
==> ./file 1 <==
1
2
==> ./file 2 <==
test
和zsh
:
zmodload zsh/mapfile
for file (**/*(D.)) printf '==> %q <==\n%s\n' $file $mapfile[$file]