How to concatenate strings formatted with printf in bash

bash, printf, shell

Solution

Use double quotes around $result and all other variables that may contain blanks and other special characters if they are to be used as a single argument to a program or built-in function:

result=$(printf '%s| %-15s| %-25s| %-15s| %-15s| %-15s\n' "$result" "$size" "$name" "$visits" "$inbound" "$outbound")

If you just want to assign the result of printf to a variable (as you did), you can also use

printf -v result '%s| %-15s| %-25s| %-15s| %-15s| %-15s\n' "$result" "$size" "$name" "$visits" "$inbound" "$outbound"

BTW: there also a += assignment operator that just appends to strings (see bash man page, section PARAMETERS).

In the full code listing, a pipe sign is missing after the 'done' before the second 'while read i'.

And when you call

echo $result

the contents of $result is already lost, since the printf is called in a sub process created by the pipe sign after 'do du ...'. The parent processes haven't access to the (environment) variables of the sub process.

I'd rather rewrite the code to something like

result=""
for name in /var/www/* ; do 
    read size __ < <(du -sh "$name")
    name=${name##*/}
    #insert the other stuff here and add arguments to printf
    printf -v line '| %-15s| %-25s\n' "$size" "$name"
    result+=$line
done
echo "$result"

The `read < <(cmd)` expression is similar to `cmd | read` but the former puts the command in the sub process instead, while the read is executed in the main process. This way, the variables set by read can be used in subsequent commands, too.

Problem

I need to concatenate several strings in loop and assign result to variable: Formatted string example: ``` result=$(printf '| %-15s| %-25s| %-15s| %-15s| %-15s\n' $size $name $visits $inbound $outbound); ``` From my view it should work like this: ``` result='' while read somevar do ... outbound=`cat "$www_path/$name/access.log"|grep \`date +"%d/%b/%Y"\`|awk '{ sum+=$11} END {print sum/1024/1024}'` result=$(printf '%s| %-15s| %-25s| %-15s| %-15s| %-15s\n' $result $size $name $visits $inbound $outbound); ... done echo $result ``` But it doesn't :( UPD: Full code listing below: ``` www_path='/var/www'; result=''; cd /var/www/; ls -d */ | while read i ; do basename "$i" ; done while read i; do du -sh "$i"| while read size name do visits=`cat "$www_path/$name/access.log"|grep \`date +"%d/%b/%Y"\`|grep -v "internal dummy connection"|awk -F ' ' '{print $1}' | sort | uniq | wc -l|tr '\n' '\t'|sed 's/$/\t/'` inbound=`cat "$www_path/$name/access.log"|grep \`date +"%d/%b/%Y"\`|grep -v "internal dummy connection"|awk '{ sum+=$10} END {print sum/1024/1024}'|tr '\n' '\t'|sed 's/$/\t\t/'` outbound=`cat "$www_path/$name/access.log"|grep \`date +"%d/%b/%Y"\`|grep -v "internal dummy connection"|awk '{ sum+=$11} END {print sum/1024/1024}'`; result=$(printf '%s| %-15s| %-25s| %-15s| %-15s| %-15s\n' "$result" "$size" "$name" "$visits" "$inbound" "$outbound") done done echo $result ```

Original source