I used to use unquoted expansion $variable when variable stored compiler flags, but I learned recently that glob metacharacters like * and ? contained in variable are still expanded, e.g.

$ f='*'
$ echo $f

Is there a portable way to just perform field splitting without globbing besides set -f .

The most explicit way I can come up with to do this in bash is to define a read_words function like so, which populates an array name with the contents of a string passed in as an argument and then uses ${arr[@]} to expand the string.


count() {
  printf '%s\n' "$#"

read_words() {
  IFS=$' \t\n' read -a "$1" <<< "$2"
  return 0

read_words arr 'a b *'
count "${arr[@]}"
  • 1
    Why not just store your flags in an array from the start? – Jesse_b Dec 13 '18 at 19:49
  • @Jesse_b ... that's a good point. I try to avoid using bash-specific features in ways that can't easily be removed, but that might not be worth the trouble. – Gregory Nisbet Dec 13 '18 at 19:52
  • Duh, sorry. True arrays are not as portable. – Jesse_b Dec 13 '18 at 19:53
  • 1
    I'm confused why set -f isn't a good answer. Are you concerned about the other expansions (brace, command, tilde, etc)? – Jeff Schaller Dec 13 '18 at 19:56
  • @JeffSchaller set -f can cause problems if the script is sourced unless the entire script is wrapped in a subshell. It's more state to keep in your head when spot-checking a script and (slightly) harder to write a mechanical check for. I'm concerned about the other expansions too; I only want to split on whitespace. ... It might be impossible to do this in a convenient way without set -f or arrays. – Gregory Nisbet Dec 13 '18 at 20:02

set -f is the most convenient way to get field splitting without globbing.

In bash, you can test whether a shell option is already set with the -o test. You may therefore do this:

if [ ! -o noglob ]; then
    set -f
    trap 'set +f' RETURN

This would detect the state of the noglob shell option (the long name for set -f). If it is not set, it is set and a RETURN trap is installed. The trap will unset the noglob shell option when the script is done, if the script is sourced. If the script is not sourced, the trap will do nothing.

Interesting note: A sourced script is very much like a shell function.


In Bash, you make the set options (contents of $-) local to a function with local -. Or you could run the part requiring set -f in a subshell, which should be portable.

countwords() { local -; set -f; set -- $1; echo "$#"; }
countwords "a b *"


countwords() ( set -f; set -- $1; echo "$#"; )
countwords "a b *"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.