8

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
2
  • 3
    Always make sure the code you post in a question reproduces the problem you are having; your original post did not. The reason result is empty is that you are assigning to it inside a subshell; those changes disappear after the subshell exits. There was no way for us to know this based on your original question. Commented Apr 13, 2013 at 23:18
  • 1
    On another note -- this is astoundingly awful code. You should never parse output from ls this way; see mywiki.wooledge.org/ParsingLs for an explanation of why -- also, doing the cat | grep three times for every single line of input is exceedingly inefficient. Commented Apr 13, 2013 at 23:26

2 Answers 2

16

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.

Sign up to request clarification or add additional context in comments.

3 Comments

it looks like the problem is not in printf but somewhere in pipes and loops. i've added full source code
I've updated the answer, the main thing is probably the access to the result env var from the topmost parent process
Is there a POSIX safe way to replaced Bash's += operator? Here's my code: ReplacementString=$(printf "%s" "\\x%d" "$ReplacementString" "CodePoint2") (I'm trying to parse the UCD) Output: U"\x%dCodePoint2",
3

Your code looks OK. One thing you do need to do, since result will contain whitespace as you add to it, is quote its expansion:

result=$(printf '...' "$result" "$size" "$name" ...)

Quoting the other variables may not be necessary, but it is usually a good idea.

Failing to quote $result shouldn't cause it to be completely blank, however. You might need to post more of the code in your while loop.

1 Comment

i've added example of variable $outbound. other variables used in printf are similar. there is nothing else in the loop

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.