-1

Have been using different bash function implementations that is able to process separate lines in arguments.

var1="
First Argument
A new line"

var2=" Second Argument A new line"

Calling the function as

theone $var

to disallow word splitting.

But then inside the function, I still have to to quote $@.

theone ()
 {
  for vl in "$@"; do
    printf '%s\n' "$vl"
  done
 }

What I want is to split by newline.

Vera
  • 1,223
  • This sounds very similar to your earlier question. Doesn't the technique answered there also work here? – doneal24 Feb 18 '23 at 18:51
  • The focus of this question is about quoting. When calling the function I do not quote the argument because I want to split the arguments by line. But then, I am forced to quote $@ in the internal loop, otherwise printf '%s\n' "$vl" will split by word. This introduces some confusion. – Vera Feb 19 '23 at 08:40

1 Answers1

0

Calling theone $var splits on every word in $var: the first argument is First, the second is Argument, the third is A, and so on. You will lose your embedded newlines with this approach, and as with any unquoted variable any word in the string that contains a shell wildcard (*, [, ], ?) will be subject to globbing and matched against names in the current directory. A string such as "Are you sure??" could be expanded to "Are you surety" if you had a file called surety in the current directory. Generally, not a good idea.

Based on your previous question I would suggest that's supposed to be written theone "$var", where word splitting does not take place and you have a single multi-line argument to theone.

If you want to process each line of $var separately I can suggest a number of different constructs. In each, the printf line is written to show clearly what is being processed in the inner loop. Call each as theone "$var":

  1. POSIX

    theone()
    {
        printf '%s\n' "$@" |
            while IFS= read -r vl
            do
                printf '## vl: %s ##\n' "$vl"
            done
    }
    
  2. Shells such as bash

    theone()
    (
        for arg in "$@"
        do
            while IFS= read -r vl
            do
                printf '## vl: %s ##\n' "$vl"
            done <<< "$arg"
        done
    )
    
  3. Also shells such as bash

    theone()
    (
        for arg in "$@"
        do
            readarray -t vls <<<"$arg"
    
        for vl in &quot;${vls[@]}&quot;
        do
            printf '## vl: %s ##\n' &quot;$vl&quot;
        done
    done
    

    )

All of these can be simplified by removing the outermost loop if you really only want to pass a single (multi-line) argument. For example, the last one could be written like this:

  1. Shells such as bash

    theone()
    {
        local vls vl
    
    readarray -t vls &lt;&lt;&lt;&quot;$1&quot;
    
    for vl in &quot;${vls[@]}&quot;
    do
        printf '## vl: %s ##\n' &quot;$vl&quot;
    done
    

    }

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • I like versions 2 and 3 because the implementations show what is being done quite clearly. Originally, I was focusing on passing a single argument, but can see the value of allowing multiple arguments. What would you suggest for multiple arguments? Would you be satisfied with version 2 ? – Vera Feb 19 '23 at 09:01
  • Is it possible that using <<< (reading from stdin) will not preserve special chars (like newline), causing troubles ? – Vera Feb 19 '23 at 10:38
  • I do not think that IFS='\n' behaves well. IFS is used for field splitting, not to determine where a record ends (there is the -d option). Shouldn't IFS be set to the empty string instead ? – Vera Feb 19 '23 at 11:13
  • In general, which would be safer and superior ? done < <(printf '%s\n' "$arg") or done <<< "$arg" ? – Vera Feb 20 '23 at 06:20
  • I am favouring the process-substitution path. – Vera Feb 20 '23 at 07:58
  • Calling theone $var splits on characters of IFS and then does globbing, it does not split on words. With IFS=$'\n'; set -o noglob; theone $var it would split on sequences of one or more newlines (ignoring leading and trailing newline). – Stéphane Chazelas Feb 20 '23 at 09:44