Search code examples
bashunixpipexcodebuildtee

pipe output to stdout and then to command then to variable


I'm working on a TeamCity server, one of my build commands is:

xcodebuild -scheme "<myscheme>" archive

I need to retrieve the .dSYM file

code=$(cat <<-'CODE'
$lines = file("php://stdin");
foreach($lines as $line){
  if(preg_match("#Touch (.*dSYM)#",$line,$m))echo "$m[1]\n";
}
CODE
)
dsym=$(xcodebuild -scheme "<myscheme>" archive | php -r "$code")

This will work. However, my issue is, I would like the logs of xcodebuild to be piped to stdout AND php -r "$code"

xcodebuild -scheme "<myscheme>" archive | tee >(php -r "$code" --)

This also works, the build log shows, and if I change php -r "$code" -- to php -r "$code" -- | cat, it logs the .dSYM file location.

But, the following doesn't work:

xcodebuild -scheme "<myscheme>" archive | tee >(dsym=$(php -r "$code" --))

#this one is the closest but is the wrong way around,
#dsym = all the output, the filename is sent to stdout
exec 5>&1
dsym=$(xcodebuild -scheme "<myscheme>" archive | tee >(php -r "$code" >&5))

And I am unable to get my head around how read -u X dsym works or is meant to be working. Does anyone know how I would go about:

  • Piping all output to stdout
  • Piping all output to an intermediate program/script (grep)
  • Storing the above intermediate program/script output into a variable

To test: save a file scheme.out and replace xcodebuild... with cat scheme.out

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus nibh
nulla, tempor nec dolor ac, eleifend imperdiet diam. Mauris tristique
congue condimentum. Nullam commodo erat fringilla vestibulum tempus.
Aenean mattis varius erat in venenatis. Donec eu tellus urna. Morbi
lacinia vulputate purus, eu egestas tortor varius eget. Curabitur
vitae commodo elit, vitae ullamcorper leo.
Touch some_test_dsym_file.dSYM
Nunc malesuada, nisi at ultricies lobortis, odio diam rhoncus urna,
sed scelerisque enim ipsum eget quam. Nunc ut iaculis sem. Pellentesque
massa odio, sodales nec lacinia nec, rutrum eu neque. Aenean quis neque
magna. Nam quis dictum quam. Proin ut libero tortor. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
Vivamus vehicula fringilla consequat. Curabitur tincidunt est sed magna
congue tristique. Maecenas aliquam nibh eget pellentesque pellentesque.
Quisque gravida cursus neque sed interdum. Proin ornare dapibus
dignissim.

Desired output

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus nibh
nulla, tempor nec dolor ac, eleifend imperdiet diam. Mauris tristique
congue condimentum. Nullam commodo erat fringilla vestibulum tempus.
Aenean mattis varius erat in venenatis. Donec eu tellus urna. Morbi
lacinia vulputate purus, eu egestas tortor varius eget. Curabitur
vitae commodo elit, vitae ullamcorper leo.
Touch some_test_dsym_file.dSYM
Nunc malesuada, nisi at ultricies lobortis, odio diam rhoncus urna,
sed scelerisque enim ipsum eget quam. Nunc ut iaculis sem. Pellentesque
massa odio, sodales nec lacinia nec, rutrum eu neque. Aenean quis neque
magna. Nam quis dictum quam. Proin ut libero tortor. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
Vivamus vehicula fringilla consequat. Curabitur tincidunt est sed magna
congue tristique. Maecenas aliquam nibh eget pellentesque pellentesque.
Quisque gravida cursus neque sed interdum. Proin ornare dapibus
dignissim.

Desired output of echo $dsym

some_test_dsym_file.dSYM

Solution

  • Your code has a lot of dependencies. I will illustrate what I think that you need without using anything beyond standard unix tools.

    This runs a command, seq 4, and sends all of its output to stdout and also sends all of its output to another command, sed 's/3/3-processed/', the output of which is captured in a variable, var:

    $ exec 3>&1
    $ var=$(seq 4 | tee >(cat >&3) | sed 's/3/3-processed/')
    1
    2
    3
    4
    

    To illustrate that we successfully captured the output of the sed command:

    $ echo "$var"
    1
    2
    3-processed
    4
    

    Explanation: var=$(...) captures the output of file handle 1 (stdout) and assigns it to var. Thus, to make the output also appear on stdout, we need to duplicate stdout to another file handle before $(...) redirects it. Thus, we use exec to duplicate stdout as file handle 3. In this way, tee >(cat >&3) sends the output of the command both the original stdout (now called 3) and to file handle 1 which is passed on the the next stage in the pipeline.

    So, using your toolchain, try:

    exec 5>&1
    dsym=$(xcodebuild -scheme "<myscheme>" archive | tee >(cat >&5) | php -r "$code")