44

How can I get a slice of $@ in Bash without first having to copy all positional parameters to another array like this?

argv=( "$@" )
echo "${argv[@]:2}";
1
  • 1
    Always quote "$@". You argv assignment would break if any args contained spaces.
    – jordanm
    Jul 7, 2013 at 3:05

4 Answers 4

63

You can use the same format as for any other array. To extract the 2nd and 3rd elements from $@, you would do:

echo "${@:1:2}"
          - -
          | |----> slice length
          |------> slice starting index 
11
  • 1
    But this seems to work on a char by char basis in v4.1.2, is there a way to do it on a word by word basis? Oct 4, 2016 at 22:08
  • @AlexejMagura I don't understand what you mean. That acts on the elements of the array. If you have one-character elements, it will work "on characters". If each element is a word, it works on words. Are you maybe trying this on a string and not an array?
    – terdon
    Oct 5, 2016 at 9:04
  • I'm trying it on a copy of "$@", which I guess might become a string at that point, I'm not sure. Oct 5, 2016 at 16:45
  • 3
    On bash 4.2.46, "${@:1:2}" actually gives me the 1st and 2nd command line arguments. Meanwhile, "${@:1}" gives me the full command line arguments, and "${@:0}" gives me the script name followed by full command line arguments.
    – Rockallite
    Feb 9, 2017 at 1:56
  • 2
    @Rockallite well, yes. The 2nd and 3rd elements of the $@ array are the 1st and 2nd arguments. "${@:1}" will print the entire array starting with the 2nd element (the 1st argument) and ${@:0} the entire array starting from the 1st element which is the name of the script. What were you expecting?
    – terdon
    Feb 9, 2017 at 9:13
10

More array slicing examples in bash

In place of using some_array_variable in your code (argv in your case), simply use @ in that variable name's place. The symbol @ represents the input argument array, and you can treat it exactly like you'd treat any other array. The confusion is simply that we are accustomed to seeing it almost always paired with the $ character like this: $@, and so we don't realize that the @ character is the array itself, and can be used alone as an array variable as well.

So, here are some examples:

Slice from the input argument array, @, which is normally seen and accessed simply as $@:

# array slicing basic format 1: grab a certain length starting at a certain
# index
echo "${@:2:5}"
#         │ │
#         │ └────> slice length
#         └──────> slice starting index 

# array slicing basic format 2: grab all remaining array elements starting at a
# certain index through to the end
echo "${@:2}"
#         │
#         │
#         └──────> slice starting index 

More array reminders to myself:

The following are general array reminders to myself that may benefit others landing on this page too.

# store a slice from an array into a new array
new_array=("${@:4}")

# print the entire array
echo "new_array = ${new_array[@]}"

Here is a runnable example of generic array slicing and array element access in bash, inspired by this source:

(Use @ as the input argument array if desired, instead of a below, according to your use case)

a=(one two three four five six)   # define a new array, `a`, with 6 elements
echo "$a"           # print first element of array a
echo "${a}"         # print first element of array a
echo "${a[0]}"      # print first element of array a
echo "${a[1]}"      # print *second* element of array a
echo "${#a[@]}"     # print number of elements in array a
echo "${a[@]:1:3}"  # print 2nd through 4th elements; ie: the 3 elements
                    # starting at index 1, inclusive, so: indices 1, 2, and 3
                    # (output: `two three four`)
echo "${a[@]:1}"    # print 2nd element onward

Run it all

Copy and paste all code chunks above into a file called array_slicing_demo.sh, and mark it executable with chmod +x array_slicing_demo.sh so that you can run it. Or, just download my demo array_slicing_demo.sh file from my eRCaGuy_hello_world repo here.

Then, run it like this:

./array_slicing_demo.sh a b c d e f g h i j k

...and you will see the following output:

b c d e f
b c d e f g h i j k
new_array = d e f g h i j k
one
one
one
two
6
two three four
two three four five six

References

  1. Bash: slice of positional parameters
  2. Single parenthesis in bash variable assignment

Keywords: array access in bash; bash array indexing; access elments in arrays in bash; bash array slicing; print arrays in bash; print array elements in bash

2

I usually do this:

somefunc() {
    local message="$1"
    shift
    echo "message = $message"
    echo "other   = $@"
}
somefunc first second third goforth

which will print:

message = first
other   = second third goforth

You can expand on the concept by shifting after second, third, etc. argument

1

For function parameters, the answer with echo "${@:1:2}" didn't work for me at all. Also, I wanted the slice-off the first element, since it was a different parameter. What did work was:

function foo(){ #takes single param + array of params
  local param1="$1".                   #first param
  local -a tmp=( "${@}" )              #copy all params
  local -a rem_params=( "${tmp[@]:1}") #slice off first:Works!
# local -a rem_params=( "${@[@]:1}"  ) #DID NOT WORK, ERROR
# local -a rem_params=( "{@:1}"      ) #DID NOT SLICE
  echo "${rem_params[@]}"
}

Maybe I'll write to test to see how this works with positional arguments for at the script-level, but no time right now.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .