如何使 bash 'pop' 和 'shift' 函数实际 return 项目弹出或移动?

How can I make bash 'pop' and 'shift' functions that actually return the item popped or shifted?

运行

$ echo $BASH_VERSION
4.3.42(1)-release

给出这两个函数:

ashift ()
{
    declare -n arr;arr=""
    ((${#arr[@]} == 0)) && return
    echo "${arr[0]"}
    arr=("${arr[@]:1}")
}
apop ()
{
    declare -n arr="";shift
    ((${#arr[@]} == 0)) && return
    echo "${arr[-1]}"
    arr=("${arr[@]:0:$((${#arr[@]}-1))}")
}

'natural' 使用它们的方式是

declare -a thearray
thearray=(a b c d e f g)
p=$(apop thearray)
s=$(ashift thearray)
echo "p=$p, thearray=${thearray[@]}, s=$s"

但是,输出不是您所期望的:

p=g, thearray=a b c d e f g, s=a

那是因为(我认为)我们在子 shell 中 运行 设置 ashiftapop 以捕获输出。如果我不捕获输出:

declare -a thearray
thearray=(a b c d e f g)
apop thearray
ashift thearray
echo "thearray=${thearray[@]}"

输出(与命令混合)是

g
a
thearray=b c d e f

那么,有人知道我如何在当前进程中 运行 apopashift 命令并捕获输出吗?


注意:为了完整起见,这些工作是因为没有捕获,所以你永远不会 运行 它们在子 shell 中:

aunshift ()
{
    declare -n arr;arr="";shift
   arr=("$@" "${arr[@]}")
}
apush ()
{
    declare -n arr;arr="";shift
    arr+=("$@")
}

原回答

根据第一条评论: 在没有中间文件或命名管道的情况下,bash(或任何在子shell 环境中运行管道组件的shell)是不可能的。

鉴于在每种情况下执行某项操作的实际代码都是单行代码,最好只学习惯用语并在线使用它。这就是我在尝试使用这些函数的所有代码中所做的。

更新

根据下面@dash-o 的解决方案,我们的想法是提供用于弹出或移动已删除值的变量。这是完整的函数集:

apush ()
{
    declare -n arr=""
    shift
    arr+=("$@")
}

apop ()
{
    declare -n array=
    shift
    declare _i=0
    if [[  =~ ^- ]]
    then
        while (($#))
        do
            if [[  = '-a' ]]
            then
                declare -n output=
                shift;shift
                continue;
            fi
            if [[  = '-n' ]]
            then
                declare c=
                shift;shift
                continue
            fi
            echo " is an invalid option"
            return 1
        done

        while ((_i<c))
        do
            ((_i+=1))
            output+=("${array[-_i]}")
        done
    else
        for _v ; do
            declare -n _var=$_v
            ((_i+=1))
            # shellcheck disable=SC2034 #https://github.com/koalaman/shellcheck/wiki/SC2034
            _var=${array[-_i]}
        done
    fi
    array=("${array[@]:0:$((${#array[@]}-_i))}")
}

aunshift ()
{
    declare -n arr=""
    shift
    arr=("$@" "${arr[@]}")
}

    ashift ()
{
    declare -n array=
    shift
    declare _i=-1
    if [[  =~ ^- ]]
    then
        while (($#))
        do
            if [[  = '-a' ]]
            then
                declare -n output=
                shift;shift
                continue;
            fi
            if [[  = '-n' ]]
            then
                declare c=
                shift;shift
                continue
            fi
            echo " is an invalid option"
            return 1
        done

        while ((_i+1<c))
        do
            ((_i+=1))
            output+=("${array[_i]}")
        done
    else
        for _v ; do
            declare -n _var=$_v
            ((_i+=1))
            # shellcheck disable=SC2034 #https://github.com/koalaman/shellcheck/wiki/SC2034
            _var=${array[_i]}
        done
    fi
    array=("${array[@]:((_i+1))}")
}

我将 _n 更改为 _i 因为该变量表示数组的索引,而不是数字或计数(而且它在视觉上与 -n 选项冲突声明)。

同样来自@dash-o 下面的回答,这里是关于如何使用它们的文档:

apush    array value1 value2 value3 ...
ashift   array value1 value2 value3 ...

apop     array var1 var2 var3 ...   # Extracted values stored in variables.
apop     array -n 3 -a tgtarray     # 3 extracted values stored in array
                                    # 'tgtarray'. Note that the values are
                                    # ADDED to any existing values already
                                    # in the 'tgtarray'.

aunshift array var1 var2 var3 ...   # Extracted values stored in variables.
aunshift array -n 4 -a tgtarray     # 4 extracted values stored in array
                                    # 'tgtarray'. Note that the values are
                                    # ADDED to any existing values already
                                    # in the 'tgtarray'.

假设您对以下 'API' 4 个操作没有问题:

apush array value1 value2 value3 ...
ashift array value1 value2 value3 ...
apop array var1 var2 var3        # Extracted values stored in variables
aunshift array var1 var2 var3    # Extract values stored in variables

可以使用 bash 引用变量(声明 -n)。

#! /bin/bash

function apush {
    declare -n _array=
    shift
    _array+=("${@}")
}

function apop {
    declare -n _array=
    shift
    declare _n=0
    for _v ; do
        declare -n _var=$_v
        let ++_n
        _var=${_array[-_n]}
    done
    array=("${_array[@]:0:$((${#_array[@]}-_n))}")
}

使用下面的测试用例

A=()
apush A B1 B2 B3
apush A C1 C2 C3
echo "S1=${A[*]}"
apop A X1 X2 X3
echo "X1=$X1, X2=$X2, X3=$X3, A=${A[*]}"

输出

S1=B1 B2 B3 C1 C2 C3
X1=C3, X2=C2, X3=C1, A=B1 B2 B3

同样,ashift、aunshift 可以按照类似的模式实现。

另请注意:由于 bash 引用的工作方式,无法访问与局部变量同名的变量。如果使用匹配本地名称的变量调用该函数,它将产生错误。函数修改为对变量使用 _ 前缀,以减少出现此问题的可能性。

apush _array ABC

Output:
bash: declare: warning: array: circular name reference
bash: warning: array: circular name reference