This other answer is good, it should be your first choice, especially if your B script does one thing and does it well (see Unix philosophy) and this "one thing" means "computing this particular variable value".
But what if the main purpose of B is to print something else? or even interact with the user? Passing additional data via stdout requires additional parsing of the retrieved result. If so, a totally independent channel of communication between B and A is highly desired. In your case one way communication is sufficient.
A temporary file is in fact quite good for it. But when you say
ugly ways, like writing out all the variables I care about in A to a file, source script B, then read everything back in from the file and restore the variables in A, besides the variable set in B that I want
you're turning the situation upside down and it's indeed ugly. The right way is to use a file to pass this one desired variable only.
Then you call B with
"$tmpf_foo" as a command line argument and refer to the file by
"$1" in B (or by another number, depending on the design). This may not be convenient if B already parses its command line arguments.
An alternative way is to
export tmpf_foo in A and refer to the file as
"$tmpf_foo" in B.
If B is a general purpose tool that can be used not only from within A, it's good to check (in B) if the file exists, before you write to it (e.g.
if [ -f "$tmpf_foo" ]; then …).
Anyway, in B you write your desired value to the file. E.g. the file content will be:
After B successfully finishes, in A you retrieve the value like this:
specificvariable=$(cat "$tmpf_foo") but without
cat; not portable though).
If you need to pass more than one variable from B to A, you may use multiple lines and read them (in A) with
read. But if you don't know in advance which variable(s) should be altered (or if any at all), then make B create lines in the file so it looks like this:
After B successfully finishes, in A you source the file:
Note you may pass any command this way (in the above example
unset is a command) and it will be executed from within A. For this reason you should be very careful while writing to the file from within B and you should make sure no other (rogue) process can inject strings to the file.
At the end (in A) you remove the temporary file with