Search code examples
bashvariablesargumentsparameter-passingstring-length

Bash: To find length of all arguments passed


I want to find the length of all my arguments passed to a script. If I do this: echo ${#1} I see the length of my first argument, and if I do this: echo $* I see all my arguments separated by spaces. So, why doesn't it work to do this: echo ${#*}? Instead this show me the number of arguments passed, as if it was echo $#.

Is there a way to echo the total length without writing the value of $* to a new variable ($allargs=$*) and then checking the length of the new variable (echo ${#allargs})?


Solution

  • Well, yes, the simplest method is:

    $ IFS=''; a=$*; echo "${#a}"
    

    Yes, there is no way to avoid a variable ($a) as the variable (parameter) is needed to be able to perform a "parameter expansion" (${#…}).

    That, above, measure the length (in UNICODE code points in bash) of the strings in $@.
    Use LC_ALL=C to count bytes.

    If you need to place "an space" between each string (assuming the first character of IFS is an space, as it is by default):

    $ a=$*; echo "${#a}"
    

    Or, to be explicit:

    $ IFS=' '; a=$*; echo "${#a}"
    

    That, of course, change the value of $IFS. There are two ways to avoid that:

    A subshell

    $ ( IFS=' '; a=$*; echo "${#a}" )
    

    A delay with eval:

    $ IFS=' ' eval 'a=$*'; echo "${#a}"
    

    For byte counting:

    $ ( LC_ALL=C; IFS=' '; a=$*; echo "${#a}" )
    $ LC_ALL=C IFS=' ' eval 'a=$*'; echo "${#a}"
    

    Yes, yes, eval is safe in this case.

    And no, arithmetic tricks will not make the answer simple:

    $ set --  one two t33; a=$*; echo "$((${#a}-$#+1))"
    9
    

    Fail with an empty "$@":

    $ set --             ; a=$*; echo "$((${#a}-$#+1))"
    1