#!/bin/bash
FOO=1
{ FOO=2; echo "a"; }
echo $FOO # prints 2
{ FOO=3; echo "b"; } | wc
echo $FOO # prints 3
How come the assignment FOO=2
is visible to the rest of the script, but the assignment FOO=3
isn't? What's special about the pipe into wc
that hides it?
And what's the neatest idiom for getting environment variables out of that group, while retaining the pipe?
Restriction: I'm not allowed to mess with the command that I'm piping into. That's a third-party tool processes its input and delivers its output in way that I can't alter. (I've written wc
here only to make a minimal repro).
Here are some related answers, but none of them address this problem:
- Pass variable from a child to parent in KSH
- Can I export a variable to the environment from a bash script without sourcing it?
- Can a shell script set environment variables of the calling shell?
Real-world scenario
My actual scenario is that I'm testing an interactive process called an "LSP server", the kind of thing that powers autocomplete+hover+gotodef inside editors like VSCode and Atom. The interactive process reads commands from stdin, and writes responses to stdout. Everyone else has been writing complicated test harnesses in typescript or python or java. But I believe bash will let me write simpler test harnesses:
{
echo "command1"
<wait for response to appear in /tmp/log>
echo "command2"
<wait for response to appear in /tmp/log>
} | lsp_server > /tmp/log
All this is working fine. But now I want to record timings:
{
T1=$(date +%s)
echo "command1"
<wait for response to appear in /tmp/log>
T2=$(date +%s)
echo "command2"
<wait for response to appear in /tmp/log>
T3=$(date +%s)
} | lsp_server > /tmp/log
echo "command1: $(( T2 - T1 )) seconds"
echo "command2: $(( T3 - T2 )) seconds"
This is running into the problem that I described.
For now I'm solving it by creating a temporary file and then sourcing it:
{
echo "T1=$(date +%s)" > /tmp/vars
echo "command1"
<wait for response to appear in /tmp/log>
echo "T2=$(date +%s)" >> /tmp/vars
echo "command2"
<wait for response to appear in /tmp/log>
echo "T3=$(date +%s)" >> /tmp/vars
} | lsp_server > /tmp/log
. /tmp/vars
echo "command1: $(( T2 - T1 )) seconds"
echo "command2: $(( T3 - T2 )) seconds"
But the idea of sourcing the file seems really crummy. I think there must be something I'm missing.