2009-08-26 3 views
124

(отредактирован, чтобы соответствовать ответ)Как разрезать массив в Баш

Глядя в разделе «Массив» в Баш (1) человек страницы, я не нашел способ ломтик массив Баш.

Так что я пришел с этой слишком сложной функции:

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

Используется так:

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

есть лучший способ сделать это?

ответ

182

См. Раздел Parameter Expansion на странице Bash man. A[@] возвращает содержимое массива, :1:2 занимает кусочек длиной 2, начиная с индекса 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 

Следует отметить, что тот факт, что «ABC» является одним из элементов массива (и что он содержит дополнительное пространство) сохранились.

+2

Прохладный. Я посмотрел в разделе «Массив» и не видел его там. –

+11

Это глупое Чэнь, почему бы это было в разделе Array? * sarc – deltaray

+0

FYI: он находится в разделе «Расширение параметров», наряду с множеством других отличных трюков. –

41

Существует также удобный ярлык для получения всех элементов массива, начиная с указанного индекса. Например, «$ {A [@]: 1}" будет «хвостом» массива, то есть массивом без его первого элемента.

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

И пока вы на нем: 'echo '$ {A [@] :: 1}" # 4' –

+3

Это замечательно, но следует отметить, что если он используется внутри функции, он должен быть слегка изменен для чтения ' $ {$ {@} [@]: 1} "'. –

0

Массив нарезка, как в Python (Из 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

Позволяет сказать, что я читаю массив из пользователей, то я хочу видеть элемент 3 до 7 как включительно.

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


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

Синтаксис среза в вашем коде идентичен синтаксису среза в 8-летнем принятом ответе. Ваш ответ не добавляет ничего нового. – melpomene