栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么在Bash中应该避免评估,我应该使用什么替代?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么在Bash中应该避免评估,我应该使用什么替代?

这个问题比眼前的问题还重要。我们将从显而易见的内容开始:

eval
具有执行“脏”数据的潜力。脏数据是指尚未重写为XYZ的任何数据;在我们的例子中,它是未格式化的任何字符串,以确保评估安全。

乍看之下,对数据进行消毒似乎很容易。假设我们要抛出一个选项列表,bash已经提供了一种清除单个元素的好方法,以及另一种将整个数组作为单个字符串进行清理的方法:

function println{    # Send each element as a separate argument, starting with the second element.    # Arguments to printf:    #   1 -> "$1n"    #   2 -> "$2"    #   3 -> "$3"    #   4 -> "$4"    #   etc.    printf "$1n" "${@:2}"}function error{    # Send the first element as one argument, and the rest of the elements as a combined argument.    # Arguments to println:    #   1 -> 'e[31mError (%d): %se[m'    #   2 -> "$1"    #   3 -> "${*:2}"    println 'e[31mError (%d): %se[m' "$1" "${*:2}"    exit "$1"}# This...error 1234 Something went wrong.# And this...error 1234 'Something went wrong.'# Result in the same output (as long as $IFS has not been modified).

现在说我们要添加一个选项以将输出重定向为println的参数。当然,我们可以只在每次调用时重定向println的输出,但是出于示例的目的,我们不会这样做。我们需要使用

eval
,因为变量不能用于重定向输出。

function println{    eval printf "$2n" "${@:3}" $1}function error{    println '>&2' 'e[31mError (%d): %se[m' "$1" "${*:2}"    exit $1}error 1234 Something went wrong.

看起来不错吧?问题是,eval解析了两次命令行(在任何shell中)。在第一次解析时,将删除一层报价。删除引号后,将执行一些可变内容。

我们可以通过在中进行变量扩展来解决此问题

eval
。我们要做的就是将所有内容都单引号,将双引号留在原处。一个例外:我们必须在之前扩展重定向
eval
,因此必须将其排除在引号之外:

function println{    eval 'printf "$2n" "${@:3}"' $1}function error{    println '&2' 'e[31mError (%d): %se[m' "$1" "${*:2}"    exit $1}error 1234 Something went wrong.

这应该工作。只要

$1
in
println
从不脏,它也是安全的。

现在稍等片刻:我一直使用与我们一直使用的相同的 未引用 语法

sudo
!为什么在这里而不是在这里工作?为什么我们必须将所有内容都单引号?
sudo
有点现代:它知道将收到的每个参数都用引号引起来,尽管这过于简单了。
eval
简单地将所有内容连接在一起。

不幸的是,没有内置的替代品可以

eval
像实参一样处理参数
sudo
eval
内置的shell
也不例外。这很重要,因为它在执行时会考虑周围代码的环境和范围,而不是像函数那样创建新的堆栈和范围。

评估替代

特定的用例通常可以替代

eval
。这是一个方便的清单。
command
代表您通常发送给的内容
eval
;随便你。

无操作

一个简单的冒号在bash中是不可操作的:

:

创建一个子外壳

( command )   # Standard notation

执行命令输出

切勿依赖外部命令。您应该始终控制返回值。将它们放在自己的行上:

$(command)   # Preferred`command`    # Old: should be avoided, and often considered deprecated# Nesting:$(command1 "$(command2)")`command "`command`"`  # Careful:  only escapes $ and  with old style, and   # special case ` results in nesting.

基于变量的重定向

在调用代码时,将

&3
(或高于的任何内容
&2
)映射到您的目标:

exec 3<&0         # Redirect from stdinexec 3>&1         # Redirect to stdoutexec 3>&2         # Redirect to stderrexec 3> /dev/null # Don't save output anywhereexec 3> file.txt  # Redirect to fileexec 3> "$var"    # Redirect to file stored in $var--only works for files!exec 3<&0 4>&1    # Input and output!

如果是一次调用,则不必重定向整个shell:

func arg1 arg2 3>&2

在调用的函数内,重定向到

&3

command <&3       # Redirect stdincommand >&3       # Redirect stdoutcommand 2>&3      # Redirect stderrcommand &>&3      # Redirect stdout and stderrcommand 2>&1 >&3  # idem, but for older bash versionscommand >&3 2>&1  # Redirect stdout to &3, and stderr to stdout: order matterscommand <&3 >&4   # Input and output!

变量间接

场景:

VAR='1 2 3'REF=VAR

坏:

eval "echo "$$REF""

为什么?如果REF包含双引号,这将中断并打开代码以供利用。可以对REF进行消毒,但是当您使用以下代码时会浪费时间:

echo "${!REF}"

没错,bash从版本2开始就内置了变量间接寻址。与

eval
您想做更复杂的事情相比,它有点棘手:

# Add to scenario:VAR_2='4 5 6'# We could use:local ref="${REF}_2"echo "${!ref}"# Versus the bash < 2 method, which might be simpler to those accustomed to eval:eval "echo "$${REF}_2""

无论如何,新方法都更直观,尽管对于习惯了这种编程的经验丰富的程序员来说似乎不是那样

eval

关联数组

关联数组在bash 4中内部实现。一个警告:它们必须使用创建

declare

declare -A VAR   # Localdeclare -gA VAR  # Global# Use spaces between parentheses and contents; I've heard reports of subtle bugs# on some versions when they are omitted having to do with spaces in keys.declare -A VAR=( ['']='a' [0]='1' ['duck']='quack' )VAR+=( ['alpha']='beta' [2]=3 )  # Combine arraysVAR['cow']='moo'  # Set a single elementunset VAR['cow']  # Unset a single elementunset VAR     # Unset an entire arrayunset VAR[@]  # Unset an entire arrayunset VAR[*]  # Unset each element with a key corresponding to a file in the   # current directory; if * doesn't expand, unset the entire arraylocal KEYS=( "${!VAR[@]}" )  # Get all of the keys in VAR

在较早版本的bash中,可以使用变量间接寻址:

VAR=( )  # This will store our keys.# Store a value with a simple key.# You will need to declare it in a global scope to make it global prior to bash 4.# In bash 4, use the -g option.declare "VAR_$key"="$value"VAR+="$key"# Or, if your version is lacking +=VAR=( "$VAR[@]" "$key" )# Recover a simple value.local var_key="VAR_$key"       # The name of the variable that holds the valuelocal var_value="${!var_key}"  # The actual value--requires bash 2# For < bash 2, eval is required for this method.  Safe as long as $key is not dirty.local var_value="`eval echo -n "$$var_value""# If you don't need to enumerate the indices quickly, and you're on bash 2+, this# can be cut down to one line per operation:declare "VAR_$key"="$value"   # Storeecho "`var_key="VAR_$key" echo -n "${!var_key}"`"   # Retrieve# If you're using more complex values, you'll need to hash your keys:function mkkey{    local key="`mkpasswd -5R0 "$1" 00000000`"    echo -n "${key##*$}"}local var_key="VAR_`mkkey "$key"`"# ...


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/408650.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号