Marek R
Marek R

Reputation: 37657

Process substitution to 'source' do not work on Mac OS

Background

I have some project where project version is defined as macros in C header file. I've found the way to fetch this data from a C header file and process that in some bash script on Mac OS.

Problem

For some reason "Process substitution" doesn't work when I feed this data to a source command.

Here is test bash script:

#!/bin/bash

VERSION_BUILD_FILE=$(dirname $0)/Somepath/Version.h

function parseNow() {
    gcc -E -dM "$VERSION_BUILD_FILE" | sed -E '{
        /^#define (VERSION_M|BUILD_NUMBER)/!d
        /#define[ \t]*[^ \t]*$/d
        s/[^ \t]*[ \t]*([^ \t]*)[ \t]*(.*)/\1=\2/
        /(""|\([^\)]*\))/d
    }'
}

function checkVersioVer() {
    echo "$VERSION_MAJOR.$VERSION_MINOR.$BUILD_NUMBER   milestone: $VERSION_MILESTONE"
}

checkVersioVer

echo parseNow result:
parseNow

echo "----"
echo "source parseNow"
source <(parseNow)
checkVersioVer

echo "----"
echo "copy parseNow then source tmp.txt"
cp <(parseNow) tmp.txt
source tmp.txt
rm tmp.txt
checkVersioVer

This produces this output:

..   milestone: 
parseNow result:
BUILD_NUMBER=1047
VERSION_MAJOR=1
VERSION_MILESTONE="v1.3"
VERSION_MINOR=3
----
source parseNow
..   milestone: 
----
copy parseNow then source tmp.txt
1.3.1047   milestone: v1.3

As you can see source parseNow part doesn't work but copy parseNow does work even though copy is performed with process substitution to a temporary file.

Here is a sample header:

#define BUILD_NUMBER 1047
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define VERSION_MILESTONE "v1.3"
// other stuff

Question

Why source <(parseNow) doesn't work (on Mac OS Mojave)? Can I fix it somehow?
I wish I don't have to use temporary file to process this C header file. This is last resort.

echo $BASH_VERSION prints 3.2.57(1)-release

Based on comments other aproches which do not work:

parseNow | source /dev/fd/0
source /dev/fd/0 < <(parseNow)
parseNow | source - # line 26: -: No such file or directory
source - < <(parseNow)

Upvotes: 2

Views: 1113

Answers (1)

Socowi
Socowi

Reputation: 27215

As explained in this answer, source does not work with process substitution in older bash versions.

However, since the workaround presented in the same answer doesn't seem to work for you, let me propose an alternative:

eval "$(parseNow)"

I know, I know, eval is bad and stuff but there really is no difference between source /dev/stdin <<< "string" and eval "string".

Personally, I think booing eval but not all the other unsafe constructs is a double standard. There is a highly interesting blog post from the author of shellcheck on this issue:
white collar eval: [[ $var -eq 42 ]] runs arbitrary code too

Upvotes: 3

Related Questions