2

I am calling find by constructing an array isufx containing filename suffixes.

Thusly, I have

echo "isufx: ${isufx[*]}" 

that results in

-name *.texi -o -name *.org -o 

Finally I got the remove the last element in the array (-o) so I can use it with find.

find "$fdir" "${isufx[@]}" 

I ask what technique to use for removing the last element that is more robust, for cases where array index does not start from 0.

3

2 Answers 2

10

With recent versions of bash (4.3 or above), you can do:

unset 'array[-1]' 

to unset the element with highest indice, like in zsh:

$ bash -c 'a[3]=1 a[12]=2; a[123123]=3; typeset -p a; unset "a[-1]"; typeset -p a' declare -a a=([3]="1" [12]="2" [123123]="3") declare -a a=([3]="1" [12]="2") 

That also works in ksh93 since ksh93t.

Note that the quotes are necessary as [...] is a glob operator in Bourne-like shells. If there was a file called array1 in the current directory for instance, an unquoted array[-1] would expand to array1, and if there wasn't that would either expand to nothing or to array[-1] or cause an error depending on the shell and glob option settings.

In zsh (where arrays are normal arrays, not those sparse arrays of ksh/bash), beside unset 'array[-1]', you can also do:

array[-1]=() 

(same for unsetting any element and shift the ones after it, while unset would set an element to the empty string when it's not the last to keep some level of compatibility with ksh).

In yash (also with normal arrays):

array -d array -1 

In fish (also with normal arrays):

set -e array[-1] 

In csh (also with normal arrays, and the first shell with array support (since the late 70s!)):

set array[$#array] = 
3
  • Note that it's important to quote the array element to be unset: otherwise you are subject to Filename Expansion -- if you have a file named "array1", then unset array[-1] will substitute the glob pattern with the filename and attempt to unset the wrong variable.CommentedJul 26, 2021 at 12:58
  • 1
    @glennjackman, yes though that's only true of Bourne-like shells in that list. In fish, [...] is not a glob operator.CommentedJul 26, 2021 at 13:00
  • Indeed, a fact that often frustrates me.CommentedJul 26, 2021 at 13:02
4

You can use array slicing to get all but the last element:

find "$fdir" "${isufx[@]:0:${#isufx[@]}-1}" 

Explanation: ${#isufx[@]} gets the number of elements in the array, and adding :0:numelements-1 to the array expansion gets numelements-1 elements starting at #0... which is all but the last one.

You could also simplify it by constructing the array slightly differently: put the extra -o at the beginning (i.e. for each suffix, add "-o" "-name" "*.$suffix" instead of "-name" "*.$suffix" "-o"), and then use "${isufx[@]:1}" to start with element #1 (skipping #0).

6
  • 1
    In this context, I sometimes find it easier to add an element so it becomes ... -o falseCommentedJul 26, 2021 at 11:24
  • @steeldriver, -false is not a standard predicate though.CommentedJul 26, 2021 at 12:58
  • Or change the array building so that you add the -o at the front, but not for the first element. You could work around the lack of -false with some hack, though GNU find seems to warn about -name / and -path "*/"... Or repeat the first or last pattern at the end of the expression, provided you have at least one.
    – ilkkachu
    CommentedJul 26, 2021 at 13:20
  • @ilkkachu -name '' should be a good approximation of -false.CommentedJul 26, 2021 at 13:28
  • Funnily enough -name / matches on / in find / -name / -prune -print but GNU find still reports: find: warning: ‘-name’ matches against basenames only, but the given pattern contains a directory separator (‘/’), thus the expression will evaluate to false all the time. Did you mean ‘-wholename’?CommentedJul 26, 2021 at 13:29

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.