Come tagliare un array in Bash


197

Guardando la sezione "Array" nella pagina man bash (1), non ho trovato il modo di dividere un array.

Quindi ho pensato a questa funzione troppo complicata:

#!/bin/bash

# @brief: slice a bash array
# @arg1:  output-name
# @arg2:  input-name
# @args:  seq args
# ----------------------------------------------
function slice() {
   local output=$1
   local input=$2
   shift 2
   local indexes=$(seq $*)

   local -i i
   local tmp=$(for i in $indexes 
                 do echo "$(eval echo \"\${$input[$i]}\")" 
               done)

   local IFS=$'\n'
   eval $output="( \$tmp )"
}

Usato così:

$ A=( foo bar "a  b c" 42 )
$ slice B A 1 2
$ echo "${B[0]}"  # bar
$ echo "${B[1]}"  # a  b c

C'è un modo migliore per farlo?


Stavo cercando come tagliare la fine di un array e sono stato diretto qui. La risposta non si trova qui e sarà un duplicato per farlo, perché ho trovato la risposta qui stackoverflow.com/questions/44939747/… . L'idea di base è che possiamo avere un'espressione aritmetica come $ {# array [@]} - (2 + 7) in cui è prevista la lunghezza nel costrutto $ {array: offset: length}. Nessuna delle risposte fornite qui lo dimostra.
Dominic108,

Risposte:


313

Vedi la sezione Espansione dei parametri nella manpagina Bash . A[@]restituisce il contenuto dell'array, :1:2accetta una porzione di lunghezza 2, a partire dall'indice 1.

A=( foo bar "a  b c" 42 )
B=("${A[@]:1:2}")
C=("${A[@]:1}")       # slice to the end of the array
echo "${B[@]}"        # bar a  b c
echo "${B[1]}"        # a  b c
echo "${C[@]}"        # bar a  b c 42
echo "${C[@]: -2:2}"  # a  b c 42 # The space before the - is necesssary

Si noti che viene conservato il fatto che "ab c" è un elemento dell'array (e che contiene uno spazio aggiuntivo).


2
Freddo. Ho guardato nella sezione Array e non l'ho visto lì.
Chen Levy,

36
È sciocco Chen, perché dovrebbe essere nella sezione Array? * sarc
deltaray,

1
@AquariusPower: creare un array di indici e affettare esso: idx=(${!A[@]}); echo ${idx[@]:1}.
In pausa fino a nuovo avviso.

7
@Feuermurmel: Fallo senza le parentesi quadre di indicizzazione:${@:1:2}
Messo in pausa fino a nuovo avviso.

5
@DennisWilliamson Ho scoperto che dovevo convertirmi $@in un array adeguato prima di fare questo o argomenti che contenevano spazi sarebbero stati divisi:ARGS=( "$@" ); ARGS_AFTER_FIRST=( "${ARGS[@]:1}" )
Heath Borders

47

C'è anche un comodo collegamento per ottenere tutti gli elementi dell'array a partire dall'indice specificato. Ad esempio "$ {A [@]: 1}" sarebbe la "coda" dell'array, ovvero l'array senza il suo primo elemento.

version=4.7.1
A=( ${version//\./ } )
echo "${A[@]}"    # 4 7 1
B=( "${A[@]:1}" )
echo "${B[@]}"    # 7 1

8
E mentre ci sei:echo "${A[@]::1}" # 4
Chen Levy,

7
Questo è fantastico, ma va notato che se usato all'interno di una funzione, deve essere leggermente modificato per leggere "${${@}[@]:1}".
Alex Gray,

@AlexGray: Questo mi dà "cattiva sostituzione" qui, ma ${@:2}funziona bene.
Nick Matteo,

3

Slice array come in Python (dalla libreria rebash ):

array_slice() {
    local __doc__='
    Returns a slice of an array (similar to Python).

    From the Python documentation:
    One way to remember how slices work is to think of the indices as pointing
    between elements, with the left edge of the first character numbered 0.
    Then the right edge of the last element of an array of length n has
    index n, for example:
    ```
    +---+---+---+---+---+---+
    | 0 | 1 | 2 | 3 | 4 | 5 |
    +---+---+---+---+---+---+
    0   1   2   3   4   5   6
    -6  -5  -4  -3  -2  -1
    ```

    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice 1:-2 "${a[@]}")
    1 2 3
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice 0:1 "${a[@]}")
    0
    >>> local a=(0 1 2 3 4 5)
    >>> [ -z "$(array.slice 1:1 "${a[@]}")" ] && echo empty
    empty
    >>> local a=(0 1 2 3 4 5)
    >>> [ -z "$(array.slice 2:1 "${a[@]}")" ] && echo empty
    empty
    >>> local a=(0 1 2 3 4 5)
    >>> [ -z "$(array.slice -2:-3 "${a[@]}")" ] && echo empty
    empty
    >>> [ -z "$(array.slice -2:-2 "${a[@]}")" ] && echo empty
    empty

    Slice indices have useful defaults; an omitted first index defaults to
    zero, an omitted second index defaults to the size of the string being
    sliced.
    >>> local a=(0 1 2 3 4 5)
    >>> # from the beginning to position 2 (excluded)
    >>> echo $(array.slice 0:2 "${a[@]}")
    >>> echo $(array.slice :2 "${a[@]}")
    0 1
    0 1

    >>> local a=(0 1 2 3 4 5)
    >>> # from position 3 (included) to the end
    >>> echo $(array.slice 3:"${#a[@]}" "${a[@]}")
    >>> echo $(array.slice 3: "${a[@]}")
    3 4 5
    3 4 5

    >>> local a=(0 1 2 3 4 5)
    >>> # from the second-last (included) to the end
    >>> echo $(array.slice -2:"${#a[@]}" "${a[@]}")
    >>> echo $(array.slice -2: "${a[@]}")
    4 5
    4 5

    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice -4:-2 "${a[@]}")
    2 3

    If no range is given, it works like normal array indices.
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice -1 "${a[@]}")
    5
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice -2 "${a[@]}")
    4
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice 0 "${a[@]}")
    0
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice 1 "${a[@]}")
    1
    >>> local a=(0 1 2 3 4 5)
    >>> array.slice 6 "${a[@]}"; echo $?
    1
    >>> local a=(0 1 2 3 4 5)
    >>> array.slice -7 "${a[@]}"; echo $?
    1
    '
    local start end array_length length
    if [[ $1 == *:* ]]; then
        IFS=":"; read -r start end <<<"$1"
        shift
        array_length="$#"
        # defaults
        [ -z "$end" ] && end=$array_length
        [ -z "$start" ] && start=0
        (( start < 0 )) && let "start=(( array_length + start ))"
        (( end < 0 )) && let "end=(( array_length + end ))"
    else
        start="$1"
        shift
        array_length="$#"
        (( start < 0 )) && let "start=(( array_length + start ))"
        let "end=(( start + 1 ))"
    fi
    let "length=(( end - start ))"
    (( start < 0 )) && return 1
    # check bounds
    (( length < 0 )) && return 1
    (( start < 0 )) && return 1
    (( start >= array_length )) && return 1
    # parameters start with $1, so add 1 to $start
    let "start=(( start + 1 ))"
    echo "${@: $start:$length}"
}
alias array.slice="array_slice"

1

Diciamo che sto leggendo un array dall'utente, quindi voglio vedere l'elemento da 3 a 7 entrambi inclusi.

cnt=0
while read var;
    do
    myarr[cnt]=$var
    cnt=$((cnt+1)) 
    done


echo ${myarr[@]:3:5}

4
La sintassi della sezione nel codice è identica a quella della risposta accettata di 8 anni. La tua risposta non aggiunge nulla di nuovo.
melpomene,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.