如何间接分配 bash 中的变量以从标准输入、文件和执行输出中获取多行数据
How do I indirectly assign a variable in bash to take multi-line data from both Standard In, a File, and the output of execution
我在这里和其他地方找到了很多片段来回答这个问题的部分内容。我什至设法以低效的方式在许多步骤中做到了这一点。如果可能的话,我真的很想找到执行此任务的单行代码,而不是必须分配给一个变量并复制几次才能执行该任务。
例如
executeToVar ()
{
# Takes Arg1: NAME OF VARIABLE TO STORE IN
# All Remaining Arguments Are Executed
local STORE_INvar="" ; shift
eval ${STORE_INvar}=\""$( "$@" 2>&1 )"\"
}
总体上确实有效,即 $ executeToVar SOME_VAR ls -l * #
实际上会用从其余参数中获取的 ls -l *
命令的执行输出填充 SOME_VAR
。但是,如果命令在末尾输出空行,(例如 - echo -e -n '\n\n123\n456\n789\n\n'
应该在开始和结束处有 2 个新行)这些被 bash 的子执行剥离过程。我在其他类似的帖子中看到,这已经通过在流的末尾添加一个标记 'x' 来解决,例如将子执行变成类似的东西:
eval ${STORE_INvar}=\""$( "$@" 2>&1 ; echo -n x )"\" # <-- ( Add echo -n x )
# and then if it wasn't an indirect reference to a var:
STORE_INvar=${STORE_INvar%x}
# However no matter how much I play with:
eval "${STORE_INvar}"=\""${STORE_INvar%x}"\"
# I am unable to indirectly remove the x from the end.
无论如何,我还需要 2 个其他变体,一个将 STDIN stream
分配给 var,另一个将文件的内容分配给 var,我认为这将是这个涉及的变体$( cat )
,或者 $( cat ${1:--} )
如果没有文件名给我一个 '-'。但是,none 的工作将一直有效,直到我能够解决确保准确分配多行变量所需的 x 的删除。
我也试过(但没用):
IFS='' read -d '' "${STORE_INvar}" <<<"$( $@ ; echo -n x )"
eval \"'${STORE_INvar}=${!STORE_INvar%x}'\"
这接近于最佳状态 -- 但删除 eval
。
executeToVar() { local varName=; shift; printf -v "" %s "$("$@")"; }
这个公式仍然存在的一个问题是 $()
去除了尾随的换行符。如果你想防止这种情况发生,你需要在子 shell 中添加你自己的尾随字符,然后自己将其删除。
executeToVar() {
local varName=; shift;
local val="$(printf %s x; "$@"; printf %s x)"; val=${val#x}
printf -v "$varName" %s "${val%x}"
}
如果你想从标准输入中读取所有内容到一个变量中,这特别容易:
# This requires bash 4.1 for automatic fd allocation
readToVar() {
if [[ && != "-" ]]; then
exec {read_in_fd}<"" # copy from named file
else
exec {read_in_fd}<&0 # copy from stdin
fi
IFS= read -r -d '' "" <&$read_in_fd # read from the FD
exec {read_in_fd}<&- # close that FD
}
...用作:
readToVar var < <( : "run something here to read its output byte-for-byte" )
...或...
readToVar var filename
测试这些:
bash3-3.2$ executeToVar var printf '\n\n123\n456\n789\n\n'
bash3-3.2$ declare -p var
declare -- var="
123
456
789
"
...和...
bash4-4.3$ readToVar var2 < <(printf '\n\n123\n456\n789\n\n')
bash4-4.3$ declare -p var2
declare -- var2="
123
456
789
"
存储在文件中有什么问题:
$ stuffToFile filename $(stuff)
其中 "stuffToFile" 测试 a。 > 1 个参数,b。输入管道
$ ... commands ... | stuffToFile filename
和
$ stuffToFile filename < another_file
其中 "stoffToFile" 是一个函数:
function stuffToFile
{
[[ -f ]] || { echo is not a file; return 1; }
[[ $# -lt 2 ]] && { cat - > ; return; }
echo "$*" >
}
所以,如果 "stuff" 有前导和尾随空行,那么你必须:
$ stuff | stuffToFile filename
我在这里和其他地方找到了很多片段来回答这个问题的部分内容。我什至设法以低效的方式在许多步骤中做到了这一点。如果可能的话,我真的很想找到执行此任务的单行代码,而不是必须分配给一个变量并复制几次才能执行该任务。
例如
executeToVar ()
{
# Takes Arg1: NAME OF VARIABLE TO STORE IN
# All Remaining Arguments Are Executed
local STORE_INvar="" ; shift
eval ${STORE_INvar}=\""$( "$@" 2>&1 )"\"
}
总体上确实有效,即 $ executeToVar SOME_VAR ls -l * #
实际上会用从其余参数中获取的 ls -l *
命令的执行输出填充 SOME_VAR
。但是,如果命令在末尾输出空行,(例如 - echo -e -n '\n\n123\n456\n789\n\n'
应该在开始和结束处有 2 个新行)这些被 bash 的子执行剥离过程。我在其他类似的帖子中看到,这已经通过在流的末尾添加一个标记 'x' 来解决,例如将子执行变成类似的东西:
eval ${STORE_INvar}=\""$( "$@" 2>&1 ; echo -n x )"\" # <-- ( Add echo -n x )
# and then if it wasn't an indirect reference to a var:
STORE_INvar=${STORE_INvar%x}
# However no matter how much I play with:
eval "${STORE_INvar}"=\""${STORE_INvar%x}"\"
# I am unable to indirectly remove the x from the end.
无论如何,我还需要 2 个其他变体,一个将 STDIN stream
分配给 var,另一个将文件的内容分配给 var,我认为这将是这个涉及的变体$( cat )
,或者 $( cat ${1:--} )
如果没有文件名给我一个 '-'。但是,none 的工作将一直有效,直到我能够解决确保准确分配多行变量所需的 x 的删除。
我也试过(但没用):
IFS='' read -d '' "${STORE_INvar}" <<<"$( $@ ; echo -n x )"
eval \"'${STORE_INvar}=${!STORE_INvar%x}'\"
这接近于最佳状态 -- 但删除 eval
。
executeToVar() { local varName=; shift; printf -v "" %s "$("$@")"; }
这个公式仍然存在的一个问题是 $()
去除了尾随的换行符。如果你想防止这种情况发生,你需要在子 shell 中添加你自己的尾随字符,然后自己将其删除。
executeToVar() {
local varName=; shift;
local val="$(printf %s x; "$@"; printf %s x)"; val=${val#x}
printf -v "$varName" %s "${val%x}"
}
如果你想从标准输入中读取所有内容到一个变量中,这特别容易:
# This requires bash 4.1 for automatic fd allocation
readToVar() {
if [[ && != "-" ]]; then
exec {read_in_fd}<"" # copy from named file
else
exec {read_in_fd}<&0 # copy from stdin
fi
IFS= read -r -d '' "" <&$read_in_fd # read from the FD
exec {read_in_fd}<&- # close that FD
}
...用作:
readToVar var < <( : "run something here to read its output byte-for-byte" )
...或...
readToVar var filename
测试这些:
bash3-3.2$ executeToVar var printf '\n\n123\n456\n789\n\n'
bash3-3.2$ declare -p var
declare -- var="
123
456
789
"
...和...
bash4-4.3$ readToVar var2 < <(printf '\n\n123\n456\n789\n\n')
bash4-4.3$ declare -p var2
declare -- var2="
123
456
789
"
存储在文件中有什么问题:
$ stuffToFile filename $(stuff)
其中 "stuffToFile" 测试 a。 > 1 个参数,b。输入管道
$ ... commands ... | stuffToFile filename
和
$ stuffToFile filename < another_file
其中 "stoffToFile" 是一个函数:
function stuffToFile
{
[[ -f ]] || { echo is not a file; return 1; }
[[ $# -lt 2 ]] && { cat - > ; return; }
echo "$*" >
}
所以,如果 "stuff" 有前导和尾随空行,那么你必须:
$ stuff | stuffToFile filename