Remove the last element from an array

前端 未结 6 1218
甜味超标
甜味超标 2020-12-13 08:55

I want to remove the last entry in my array, and I want the array to show me that it has 1 less entry when I am using the ${#array[@]}. This is the current line

相关标签:
6条回答
  • 2020-12-13 09:19

    The following works fine for Mac/bash@3.x and Linux (ubuntu/bash@4.x)

    unset arr[$[${#arr[@]}-1]] # non-sparse array only
    

    in more details:

    len=${#arr[@]}
    idx=$[$len-1]    # <=> $(($len-1))
    unset arr[$idx]
    
    0 讨论(0)
  • 2020-12-13 09:27

    If you'd like an answer which won't eat your kittens, try this:

    array=([1]=1 {2..5} [10]=6);
    # declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [5]="5" [10]="6}")'
    index=("${!array[@]}");
    # declare -a index='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="10")'
    unset 'array[${index[@]: -1}]';
    # declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [5]="5")'
    

    And there you have it - removing the last element. Now I'll present a much easier answer which probably meets your needs but has a caveat:

    array=([1]=1 {2..5} [10]=6);
    # declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [5]="5" [10]="6}")'
    array=("${array[@]::${#array[@]}-1}");
    # declare -a array='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")'
    

    This version takes a shortcut. It re-indexes the array and drops the last element. Unfortunately you can also see that the index has not been maintained. The values and their order has been. If you don't care about the index then this is probably the answer you wanted.

    Both of the above answers will also work on bash 4 Associative Arrays.

    --

    The chosen answer is not safe. Here's an example:

    array=([1]=1 {2..5} [10]=6);
    # declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [5]="5" [10]="6")'
    unset 'arr[${#arr[@]}-1]';
    # declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [10]="6")'
    

    Okay so as you can see it is unsetting the element with index 5 because it incorrectly calculated the index of the last element of the array. It failed because it operated on an assumption that all arrays are zero-based, and not sparse. This answer will fail on arrays starting with anything other than zero, arrays which are sparse, and obviously must fail for an associative array with 'fubar' for the last element.

    0 讨论(0)
  • 2020-12-13 09:31

    In your function, you could add the following:

    target="${@:$(($#)):1}"
    set -- "${@:1:$(($#-1))}"
    
    0 讨论(0)
  • 2020-12-13 09:33

    You must remove the blank before -1.

    0 讨论(0)
  • 2020-12-13 09:36

    For any indexed array (sparse or not), since bash 4.3+ (and ksh93+), this is the simplest of solutions:

    unset 'array[-1]'
    

    The quotes are needed to avoid shell expansion in bash if the -1 is an arithmetic expression or a variable. This also works correctly:

    a=3; unset 'arr[ a - 4 * 1 ]'
    

    But will not work if unquoted ('') as the * will be expanded to the list of files in the present working directory ($pwd).

    For older bash versions: this works since bash 3.0 for non-sparse arrays:

    unset 'arr[ ${#arr[@]}-1 ]'
    

    Example:

    $ arr=( {a..i} ); declare -p arr  
    declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g" [7]="h")
    $ unset 'arr[ ${#arr[@]}-1 ]'; declare -p arr
    declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g")
    

    This will not work for sparse arrays (with some holes):

    $ arr=( {a..g} [9]=i ); declare -p arr
    declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g" [9]="i")
    $ unset 'arr[ ${#arr[@]}-1 ]'; declare -p arr
    declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g" [9]="i")
    

    This happens because the count of elements (${#arr[@]}) is 8 and 8-1 is 7.
    So, the command will unset arr[7], which doesn't exist. Nothing is done.

    A solution, that also work for Associative arrays (in whatever it could mean "the last element" in an unsorted list) is to generate a new array of indexes.
    Then use the last index to unset that element.

    Assuming arr is already defined (for bash 3.0+):

    $ index=( "${!arr[@]}" )          # makes index non-sparse.
    $ unset 'arr[${index[@]}-1]'      # unset the last index.
    $ declare -p arr
    declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g")
    

    A slightly more portable (works in ksh93), that looks ugly, solution is:

    $ arr=( {a..e} [9]=i )
    $ index=( "${!arr[@]}" )
    $ unset "arr[  ${index[${#index[@]}-1]}  ]"   # Yes, double quotes.
    $ declare -p arr
    declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e")   
    

    Or (again, double quotes for ksh):

    $ unset "arr[${index[@]: -1}]"
    

    If you want to avoid the space and the negative number, make it a variable:

    $ a="-1"; unset "arr[${index[@]:a}]"
    
    0 讨论(0)
  • 2020-12-13 09:40

    The answer you have is (nearly) correct for non-sparse indexed arrays¹:

    unset 'arr[${#arr[@]}-1]'
    

    Bash 4.3 or higher added this new syntax to do the same:

     unset arr[-1]
    

    (Note the single quotes: they prevent pathname expansion).

    Demo:

    arr=( a b c )
    echo ${#arr[@]}
    

    3

    for a in "${arr[@]}"; do echo "$a"; done
    
    a
    b
    c
    
    unset 'arr[${#arr[@]}-1]'
    for a in "${arr[@]}"; do echo "$a"; done
    
    a
    b
    

    Punchline

    echo ${#arr[@]}
    
    2
    

    (GNU bash, version 4.2.8(1)-release (x86_64-pc-linux-gnu))


    ¹ @Wil provided an excellent answer that works for all kinds of arrays

    0 讨论(0)
提交回复
热议问题