如何使 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 中 运行 设置 ashift
和 apop
以捕获输出。如果我不捕获输出:
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
那么,有人知道我如何在当前进程中 运行 apop
和 ashift
命令并捕获输出吗?
注意:为了完整起见,这些工作是因为没有捕获,所以你永远不会 运行 它们在子 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
运行
$ 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 中 运行 设置 ashift
和 apop
以捕获输出。如果我不捕获输出:
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
那么,有人知道我如何在当前进程中 运行 apop
和 ashift
命令并捕获输出吗?
注意:为了完整起见,这些工作是因为没有捕获,所以你永远不会 运行 它们在子 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