2009-08-26 27 views
124

(editado para ajustarse a la respuesta)Cómo cortar una matriz en bash

Mirando la sección "Matriz" en el (1) página de manual de bash, no he encontrado una manera de cortar una matriz bash.

Así me ocurrió con esta función demasiado complicado:

#!/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)" 
} 

Se utiliza la siguiente manera:

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

¿Hay una mejor manera de hacer eso?

Respuesta

182

Consulte la sección Parameter Expansion en la página de Bash man. A[@] devuelve el contenido de la matriz, :1:2 toma una rebanada de longitud 2, comenzando en el índice 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 

Tenga en cuenta que el hecho de que "abc" es un elemento de array (y que contiene un espacio extra) es Preservado.

+2

fresca. Busqué en la sección Array y no la vi allí. –

+11

Eso es tonto Chen, ¿por qué sería en la sección Array? * sarc – deltaray

+0

FYI: está en la sección "Expansión de parámetros", junto con muchos otros trucos ingeniosos. –

41

Hay también un acceso directo conveniente de obtener todos los elementos de la matriz a partir de índice especificado. Por ejemplo, "$ {A [@]: 1}" sería la "cola" de la matriz, es decir la matriz sin su primer elemento.

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

Y ya que en ella: 'echo "$ {A [@] :: 1}" # 4' –

+3

Esto es muy bueno, pero hay que señalar que si se utiliza dentro de una función, debe ser alterada ligeramente para leer'" $ {$ {@} [@]: 1} "'. –

0

matriz rebanar como en Python (De la biblioteca 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" 
0

Digamos que estoy leyendo una matriz a partir de usuario, entonces quiero ver elemento 3 al 7 ambos inclusive.

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


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

La sintaxis de sector en su código es idéntica a la de la respuesta aceptada de 8 años. Tu respuesta no agrega nada nuevo. – melpomene