我有一个带有多个参数的命令:
my_command --arg1 <arg1> \
--arg2 <arg2> \
--arg3 <arg3>
我将 的调用包装my_command
在一个函数中,该函数设置各种参数的值,这些参数可能会被传递,也可能不会被传递:
run_my_command() {
if [[ ... ]]; then
ARG1FLAGS="--arg1 $ARG1"
else
ARG1FLAGS=''
fi
if [[ ... ]]; then
ARG2FLAGS="--arg2 $ARG2"
else
ARG2FLAGS=''
fi
if [[ ... ]]; then
ARG3FLAGS="--arg3 $ARG3"
else
ARG3FLAGS=''
fi
my_command $ARG1FLAGS \
$ARG2FLAGS \
$ARG3FLAGS
}
在某些情况下,有必要将文件名参数传递给my_command
:
run_my_command() {
# ...
if [[ ... ]]; then
FILEARG="--input_file $SOME_FILE"
else
FILEARG=''
fi
my_command $FILEARG \
...
}
所有这些都运行良好。现在,我想有条件地使用进程替换FILEARG
,但这当然行不通:
run_my_command() {
# ...
if [[ ... ]]; then
FILEFLAG='--input_file'
FILEARG=<(other_cmd)
else
FILEARG=''
fi
my_command $FILEFLAG $FILEARG \
...
}
因为到运行时my_command
,$FILEARG
,一个看起来像 的匿名管道/dev/fd/63
已经关闭。
现在我已经通过将所有内容my_command
放入条件来解决这个问题:
run_my_command() {
# Get ARG1, ARG2, ARG3...
if [[ ... ]]; then
my_command --input_file <(other_cmd) \
$ARG1FLAGS \
$ARG2FLAGS \
$ARG3FLAGS
else
my_command $ARG1FLAGS \
$ARG2FLAGS \
$ARG3FLAGS
fi
}
但我不喜欢重复。我觉得也许我可以用 做一些事情eval
,或者我可以将其放入my_command
另一个函数中,但我还没有弄清楚。如何有条件地使用进程替换来生成一个my_command
可以读取的文件,而无需复制整个调用my_command
?
请注意,我运行的是 bash 4.4.19。令我相当惊讶的是,bash 3.2.57 的行为似乎确实符合下面的 Bash Hackers wiki 建议的方式:
doit() {
local -r FOO=<(echo hi)
cat $FOO
}
doit
# bash 3.2.57:
$ ./test.sh
hi
# bash 4.4.19:
$ ./test.sh
cat: /dev/fd/63: Bad file descriptor
以下是我检查过的一些问题,但我无法从中得到有效的答案:
另外,Bash 黑客维基注释相当神秘的是:
如果进程替换被扩展为函数的参数,在函数调用期间扩展为环境变量,或扩展为函数内的任何赋值,则进程替换将“保持打开”状态,以供函数内的任何命令使用或其被调用者,直到设置它的函数返回。如果在被调用者中再次设置相同的变量,除非新变量是本地变量,否则先前的进程替换将关闭,并且当被调用者返回时,调用者将无法使用该变量。
本质上,扩展到函数内变量的进程替换保持打开状态,直到发生进程替换的函数返回 - 即使分配给由函数调用者设置的局部变量也是如此。动态作用域并不能保护它们不被关闭。
但我无法仅通过将进程替换分配给函数中的变量来“保持打开状态”。
答案1
这更像是回避问题,但是如果将其他参数放在数组中而不是单个变量中,则重复会更少。$ARGnFLAGS
在任何情况下,这也是正确的做法,因为您可以获得命令的参数,而不会妨碍分词或文件名通配。
run_my_command() {
args=()
if [[ ... ]]; then
args+=(--arg1 "$ARG1")
fi
if [[ ... ]]; then
args+=(--arg2 "$ARG2")
fi
if [[ ... ]]; then
my_command --input_file <(other_cmd) "${args[@]}"
else
my_command "${args[@]}"
fi
}
答案2
run_my_command() {
# ...
if [[ ... ]]; then
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' RETURN
other_cmd >"$tmpfile"
FILEFLAG='--input_file'
FILEARG="$tmpfile"
else
FILEARG=''
fi
my_command $FILEFLAG $FILEARG \
...
return
}
...或类似的东西。
也就是说,使用临时文件而不是尝试使用进程替换。
答案3
您可以使用以下方法eval
来获得您想要的东西:
run_my_command() {
# Get ARG1, ARG2, ARG3...
commandline="my_command $ARG1FLAGS $ARG2FLAGS $ARG3FLAGS"
if [[ ... ]]; then
commandline="$commandline --input_file <(other_cmd)"
fi
eval $commandline
}