4

I have a problem with my first bash script. I fill an array in for loop and when I try to get an item from it I always get the first element.

for (( i = 0; i < ${#*}; i++ )); do
hash=$(md5 -q ${@:$i:1})
modifiedNames[$i]=${@:$i:1}$hash 
done

echo ${modifiedNames[1]}

for instance if I call my script like this: ./script.sh file1 file2 i get file1[file1hash]

Thanks in advance!

4 Answers 4

2

I think your loop is behaving funny because it should start with i = 1 and go to i = ${#*}. Expansion of ${@:0:1} is giving file1 and so is ${@:1:1}.

Try for (( i = 1; i <= ${#*}; i++ ))

Sign up to request clarification or add additional context in comments.

Comments

1

$@ isn't a real array - it's a shell "Special Parameter", and you need to be a bit more careful with it than other arrays.

The reason for the behaviour you're seeing is that the exact behaviour of the ${parameter:length:offset} syntax is special-cased when parameter is @, and the behaviour is not consistent with the behaviour you'd get if @ was a real array.

Here's the relevant documentation (bold emphasis mine):

${parameter:offset:length}

... If parameter is @, the result is length positional parameters beginning at offset. If parameter is @, the result is length positional parameters beginning at offset. ...

The positional parameters are $0, $1, $2, ..., so with this syntax it's behaving as if $@ contained the script name ($0) as well as the parameters to the script ($1, $2, ...). This is inconsistent with "$@" expanding to "$1" "$2" ..., but that's life.

You should be able to simplify things (and fix the script) by making a new array instead of using $@ directly, i.e.

new_array=("$@")
for (( i = 0; i < ${#new_array}; i++ )); do
    hash=$(md5 -q ${new_array[@]:$i:1})
    modifiedNames[$i]=${new_array[@]:$i:1}$hash 
done

echo ${modifiedNames[1]}

Comments

1

You are iterating through the arguments array starting at 0, which is the command name. So, if you call your script like ./script.sh file1 file2, then:

${@} = array(
   [0] = ./script,
   [1] = file1,
   [2] = file2
)

So you are getting:

modifiedNamed = array(
   [0] = md5(./script),
   [1] = md5(file1)
)

You should change your for loop indexes to:

for (( i = 1; i <= ${#*}; i++ )); do

Comments

0

Actually, you are getting the second element, but your loop puts the same value in [0] and in [1].

I'm not certain what you are trying to do but it would probably be easier to iterate over the arguments and then keep a separate counter for the array.

n=0
for i in "$@"; do
  . . .
  n=$(($n + 1))
done

1 Comment

Ok, but why loop behaves so? I can't see the reason why it puts the same value to array items.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.