bash 函数像其他语言一样接受参数?

bash 函数像其他语言一样接受参数?

我有一个 bash 函数可以$PATH这样设置——

assign-path()
{
    str=$1
    # if the $PATH is empty, assign it directly.
    if [ -z $PATH ]; then
        PATH=$str;
    # if the $PATH does not contain the substring, append it with ':'.
    elif [[ $PATH != *$str* ]]; then
        PATH=$PATH:$str;
    fi
}

但问题是,我必须为不同的变量编写不同的函数(例如,为$CLASSPATH类似的函数编写另一个函数assign-classpath()等)。我找不到将参数传递给 bash 函数的方法,以便我可以通过引用访问它。

如果我有类似的东西那就更好了——

assign( bigstr, substr )
{
    if [ -z bigstr ]; then
        bigstr=substr;
    elif [[ bigstr != *str* ]]; then
        bigstr=bigstr:substr;
    fi
}

知道如何在 bash 中实现类似上面的功能吗?

答案1

bash可以使用${!varname}它来扩展另一个变量引用的内容。例如:

$ var=hello
$ foo () { echo "${!1}"; }
$ foo var
hello

从手册页:

${!prefix*}
${!prefix@}
       Names matching prefix.  Expands to the names of variables whose names
       begin with prefix, separated by the first character of the IFS special
       variable.  When @ is used  and the expansion appears within double quotes,
       each variable name expands to a separate word.

另外,要设置内容引用的变量(没有 的危险eval),您可以使用declare。例如:

$ var=target
$ declare "$var=hello"
$ echo "$target"
hello

因此,您可以像这样编写函数(请小心,因为如果您declare在函数中使用,则必须给出-g,否则变量将是局部的):

shopt -s extglob

assign()
{
  target=$1
  bigstr=${!1}
  substr=$2

  if [ -z "$bigstr" ]; then
    declare -g -- "$target=$substr"
  elif [[ $bigstr != @(|*:)$substr@(|:*) ]]; then
    declare -g -- "$target=$bigstr:$substr"
  fi
}

并像这样使用它:

assign PATH /path/to/binaries

请注意,我还纠正了一个错误,其中 ifsubstr已经是 的冒号分隔成员之一的子字符串bigstr,但不是其自己的成员,则不会添加它。例如,这将允许添加/binPATH已经包含/usr/bin.它使用extglob集合来匹配字符串的开头/结尾或冒号,然后匹配其他任何内容。如果没有extglob,替代方法是:

[[ $bigstr != $substr && $bigstr != *:$substr &&
   $bigstr != $substr:* && $bigstr != *:$substr:* ]]

答案2

bash 4.3 中的新功能是&-n选项:declarelocal

func() {
    local -n ref="$1"
    ref="hello, world"
}

var='goodbye world'
func var
echo "$var"

打印出来的是hello, world.

答案3

您可以用来eval设置参数。可以找到该命令的说明这里。以下的用法eval是错误的:

错误的(){
  评估 $1=$2
}

关于附加评估,eval您应该使用

分配(){
  评估 $1='$2'
}

检查使用这些函数的结果:

$X1='$X2'
$X2='$X3'
$ X3='xxx'
$
$回声:$X1:
:$X2:
$回声:$X2:
:$X3:
$回声:$X3:
:xxx:
$
$ 错误 Y $X1
$回声:$Y:
:$X3:
$
$ 分配 Y $X1
$回声:$Y:
:$X2:
$
$ 分配 Y “hello world”
$回声:$Y:
:你好世界:
$#以下内容可能会出乎意料
$ 分配 Z $Y
$回显“:$Z:”
:你好:
$ # 因此如果第二个参数是变量,则必须引用它
$ 分配 Z "$Y"
$回显“:$Z:”
:你好世界:

但是您可以在不使用 的情况下实现您的目标eval。我更喜欢这种更简单的方式。

以下函数以正确的方式进行替换(我希望)

增加(){
  本地电流=$1
  本地增量=$2
  本地新
  如果 [[ -z $CURRENT ]];然后
    新=$增强
  埃利夫[[! ( ( $CURRENT = $AUGMENT ) || ( $CURRENT = $AUGMENT:* ) || \
    ( $当前 = *:$AUGMENT ) || ( $当前 = *:$AUGMENT:* ) ) ]];然后
    新=$当前:$增强
  别的
    新=$当前
  回显“$新”
}

检查以下输出

扩充 /usr/bin /bin
/usr/bin:/bin

扩充 /usr/bin:/bin /bin
/usr/bin:/bin

扩充 /usr/bin:/bin:/usr/local/bin /bin
/usr/bin:/bin:/usr/local/bin

增强 /bin:/usr/bin /bin
/bin:/usr/bin

增强 /bin /bin
/垃圾桶


扩充 /usr/bin: /bin
/usr/bin::/bin

扩充 /usr/bin:/bin: /bin
/usr/bin:/bin:

扩充 /usr/bin:/bin:/usr/local/bin:/bin
/usr/bin:/bin:/usr/local/bin:

增强 /bin:/usr/bin: /bin
/bin:/usr/bin:

增强/bin:/bin
/垃圾桶:


增强:/bin
::/垃圾桶


扩充“/usr lib”“/usr bin”
/usr lib:/usr bin

扩充“/usr lib:/usr bin”“/usr bin”
/usr lib:/usr bin

现在您可以augment通过以下方式使用该函数来设置变量:

PATH=`扩充 PATH /bin`
CLASSPATH=`增加 CLASSPATH /bin`
LD_LIBRARY_PATH=`扩展 LD_LIBRARY_PATH /usr/lib`

答案4

assign () 
{ 
    if [ -z ${!1} ]; then
        eval $1=$2
    else
        if [[ ${!1} != *$2* ]]; then
            eval $1=${!1}:$2
        fi
    fi
}

$ echo =$x=
==
$ assign x y
$ echo =$x=
=y=
$ assign x y
$ echo =$x=
=y=
$ assign x z
$ echo =$x=
=y:z=

这个合适吗?

相关内容