Score:-1

Bash function processing on arrays

jp flag

Why does bash complain with

bash: warning: command substitution: ignored null byte in input

when calling the following function.

ary=( "PROGL" "TYPOG" )
seqn=( "THIS" "THAT" )
outa=$( my_func "${ary[@]}" "${seqn[@]}" )

Here is the function

my_func ()
 {
  ##
  declare -n incl="$1" excl="$2"

  local isufx=()

  if (( ${#incl[@]} > 0 )); then

    for ext in "${incl[@]}"; do

      if [[ "$ext" == "PROGL" ]]; then
        isufx+=( --include=\*.{rc,el,c,f} )
        continue
      elif [[ "$ext" == "PEXTD" ]]; then
        isufx+=( --include=\*.{rc,el,c,f} )
        isufx+=( --include=\*.{cp,cpp,f90,f95,f03,f08} )
        continue
      elif [[ "$ext" == "TYPOG" ]]; then
        isufx+=( --include=\*.{org,texi,tex} )
        continue
      fi  

  printf '%s\0' "${isufx[@]}"
 }

Using Ubuntu 18.04.

Would it be better to use typeset rather than declare?

ar flag
Which operating system and version are you using?
jp flag
I am using Ubuntu 18.04
Score:3
jp flag

This seems like a typical XY problem question ... So, all I can do to help is to highlight some points you mentioned and hopefully this will at least help you avoid dead ends and point you away from the wrong direction.

Hopefully, this will help you as well to see how many separate, focused, clear and individual questions are included in your single general question above so that in the future you might break them down and ask for each one separately if you wish.

First,

The option -n is used to declare a reference variable nameref and cannot be used to declare an array ... Please see the bash builtin declare ... Please, see the demonstration below:

$ array=( "word1" "word2 space" "word3 *" )

# This will throw an error:

$ function my_func {
  local -n my_array=( "$@" )
  my_array+=("This" "That")
  printf '%s\n' "${my_array[@]}"
}

$ my_func "${array[@]}"
bash: local: my_array: reference variable cannot be an array
word1
word2 space
word3 *
This
That


# This will not work:

$ function my_func {
  local -n my_array="$1"
  my_array+=("This" "That")
  printf '%s\n' "${my_array[@]}"
}

$ my_func "${array[@]}"
This
That

You can, however, use a nameref and pass the array/s names(not elements) as positional arguments, but this has its own disadvantage as well ... Because, you're not actually recreating the passed array/s as local array/s inside your function, but rather merely referencing the original array/s by another name and therefore changes are not "local to your function" as you might think ... Please, see the demonstration below:

$ array=( "word1" "word2 space" "word3 *" )
$ array2=( "word4" "word5" "word6" )

$ function my_func {
  local -n my_array="$1"
  local -n my_array2="$2"
  my_array+=("This" "That")
  my_array2+=("This2" "That2")
  printf '%s\n' "${my_array[@]}"
  printf '%s\n' "${my_array2[@]}"
}

# Run the function:

$ my_func "array" "array2"
word1
word2 space
word3 *
This
That
word4
word5
word6
This2
That2

# Run the same function again:

$ my_func "array" "array2"
word1
word2 space
word3 *
This
That
This
That
word4
word5
word6
This2
That2
This2
That2

# Print original arrays

$ echo "${array[@]}"
word1 word2 space word3 * This That This That

$ echo "${array2[@]}"
word4 word5 word6 This2 That2 This2 That2

Second,

Passing array elements as arguments to a function with my_func "${array[@]}" will not pass them as an array but rather as positional arguments i.e. first element as $1, second element as $2 ... and so on ... Therefore you should rebuild the array inside the function with array=( "$@" ) ... See the demonstration below:

$ array=( "word1" "word2 space" "word3 *" )

# This will work:

$ function my_func {
  local my_array=( "$@" )
  my_array+=("This" "That")
  printf '%s\n' "${my_array[@]}"
}

$ my_func "${array[@]}"
word1
word2 space
word3 *
This
That

# This will not work:

$ function my_func {
  local my_array=( "$1" )
  my_array+=("This" "That")
  printf '%s\n' "${my_array[@]}"
}

$ my_func "${array[@]}"
word1
This
That

Needless to say that if you pass more than one array to the function, they will be hard to split back into original separate arrays and they will be combined ... Please, see the demonstration below:

$ array=( "word1" "word2 space" "word3 *" )
$ array2=( "word4" "word5" "word6" )

$ function my_func {
  local my_array=( "$@" )
  my_array+=("This" "That")
  printf '%s\n' "${my_array[@]}"
}

$ my_func "${array[@]}" "${array2[@]}"
word1
word2 space
word3 *
word4
word5
word6
This
That

Third,

You don't really need to handle an array in your script like this in order to make it available inside a function ... If an array(or variable) is defined in your script, then you can just call it directly inside your function like so:

$ array=( "word1" "word2 space" "word3 *" )

$ function my_func {
  local my_array=( "${array[@]}" )
  my_array+=("This" "That")
  printf '%s\n' "${my_array[@]}"
}

$ my_func
word1
word2 space
word3 *
This
That

Fourth,

With proper quoting(as it should be in most cases), array expansion will pass each element as a single token ... Therefore you cant assign command options and their argument/s to an array element and expect it to work as usual in a command ... Please, see the demonstration below:

# This works:

$ echo -e 'new\nline'
new
line

# This doesn't:

$ array=( "-e 'new\nline'" )

$ echo "${array[@]}"
-e 'new\nline'

Fifth,

As for the warning message you see:

bash: warning: command substitution: ignored null byte in input.

That is actually the least of your problems ... It, however, simply means that you/your commands asked to include null byte/s(i.e. \0 from printf '%s\0' "${isufx[@]}" in your function that you used in the command substitution) in/during a string creation/definition process(e.g. in the command substitution syntax) that will need its end to be identified in order to be handled properly later on ... and bash can't do that as it actually uses C-style strings(which are terminated by a null character '\0') and therefor bash ignores that and informs you about it ... So, simply change that and the message shouldn't appear anymore.

jp flag
I have read that passing arrays to a function is usually done with a `nameref`. But I am totally unaware of how such approach is to be implemented.
Score:0
cn flag

You expicitly print a \0 byte inside a command substitution. To overly simplify, this is what you effectively do:

outa=$( printf '\0' )

which command on its own also prints the same warning.

Shell variables are represented internally as \0-terminated strings, therefore cannot contain the \0 byte as part of their actual value. Thus, when a command inside the command substitution emits this byte, bash has to strip it off from the value assigned to the variable. Hence the warning.

Score:0
it flag

Consider using path-like variables (colon-separated lists) instead of arrays, if your data doesn't contain colons. I use Stephen Collyer's bash_path_funcs, described in Linux Journal way back in 2000:

https://www.linuxjournal.com/article/3645 https://www.linuxjournal.com/article/3768 https://www.linuxjournal.com/article/3935

The addpath function adds an entry to a path only if it is not there in the first place. delpath -n deletes all non-existent directories from a path.

You can get the pathfunc.tgz file from https://web.archive.org/web/20061210054813/http://www.netspinner.co.uk:80/Downloads/pathfunc.tgz

I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.