Running
$ echo $BASH_VERSION
4.3.42(1)-release
given these two functions:
ashift ()
{
declare -n arr;arr="$1"
((${#arr[@]} == 0)) && return
echo "${arr[0]"}
arr=("${arr[@]:1}")
}
apop ()
{
declare -n arr="$1";shift
((${#arr[@]} == 0)) && return
echo "${arr[-1]}"
arr=("${arr[@]:0:$((${#arr[@]}-1))}")
}
the 'natural' way to use them would be
declare -a thearray
thearray=(a b c d e f g)
p=$(apop thearray)
s=$(ashift thearray)
echo "p=$p, thearray=${thearray[@]}, s=$s"
However, the output is not what you would expect:
p=g, thearray=a b c d e f g, s=a
That is because (I think) we are running the ashift
and apop
in a subshell to capture the output. If I do not capture the output:
declare -a thearray
thearray=(a b c d e f g)
apop thearray
ashift thearray
echo "thearray=${thearray[@]}"
the output (intermixed with the commands) is
g
a
thearray=b c d e f
So, does anyone know how I can run the apop
and ashift
commands in the current process AND capture the output?
Note: For completeness, these work because there is no capturing, so you don't ever run them in a subshell:
aunshift ()
{
declare -n arr;arr="$1";shift
arr=("$@" "${arr[@]}")
}
apush ()
{
declare -n arr;arr="$1";shift
arr+=("$@")
}
As per the first comment: Not possible on bash (or any shell that runs pipeline components in a subshell environment) without an intermediate file or a named pipe.
Given that the actual code that does something in each case is a one-liner, it's best to just learn the idiom and use it in-line. That is what I have done in all of the code I have that was trying to use these functions.
As per the solution from @dash-o below, the idea is to provide variables in which to pop or shift the removed values. Here is the full set of functions:
apush ()
{
declare -n arr="$1"
shift
arr+=("$@")
}
apop ()
{
declare -n array=$1
shift
declare _i=0
if [[ $1 =~ ^- ]]
then
while (($#))
do
if [[ $1 = '-a' ]]
then
declare -n output=$2
shift;shift
continue;
fi
if [[ $1 = '-n' ]]
then
declare c=$2
shift;shift
continue
fi
echo "$1 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="$1"
shift
arr=("$@" "${arr[@]}")
}
ashift ()
{
declare -n array=$1
shift
declare _i=-1
if [[ $1 =~ ^- ]]
then
while (($#))
do
if [[ $1 = '-a' ]]
then
declare -n output=$2
shift;shift
continue;
fi
if [[ $1 = '-n' ]]
then
declare c=$2
shift;shift
continue
fi
echo "$1 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))}")
}
I changed the _n
to _i
because the variable represents an index into the array, not a number or a count (and it was too visually clashing with the -n
option to declare).
Also from @dash-o's answer below, here are the docs in how to use them:
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'.