0

Apologies if this is a basic question - I'm stuck trying to solve a larger problem, and it's come down to how a shell script is invoked - directly (shellScript.sh) or using sh shellScript.sh.

Here's a model for the problem:

When I execute on bash:

cat <(echo 'Hello')

I see the output

Hello

But when I use:

sh -c "cat <(echo 'Hello')"

I see errors:

sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `cat <(echo 'Hello')'

I've tried escaping the <, ( and ) in various combinations, but I don't see the output anywhere. What am I missing here?

My actual problem is that I'm passing a <() as an input argument to a python script within a shell script, and while it works fine when I invoke the shell script using just the name, if I use sh to invoke it, I get errors similar to what I've shown above.

Thank you!

  • 1
    try bash -c "cat <(echo 'Hello')". If you're using Debian/Ubuntu, you most likely have dash as your /bin/sh. see: lwn.net/Articles/343924 – Tim Kennedy May 9 '17 at 20:27
  • It doesn't even matter if sh is linked to bash, when invoked as sh it operates in "POSIX mode" and wouldn't support that feature – Eric Renouf May 9 '17 at 20:44
  • @steeldriver, bash disables <(...) when invoked as sh. – Stéphane Chazelas May 9 '17 at 21:08
4

Process substitution is a feature that originated in the Korn shell in the 80s (in ksh86). At the time, it was only available on systems that had support for /dev/fd/<n> files.

Later, the feature was added to zsh (from the start: 1990) and bash (in 1993). zsh was using temporary named pipes to implement it, while bash was using /dev/fd/<n> where available and named pipes otherwise. zsh switched to using /dev/fd/<n> where available in 2.6-beta17 in 1996.

Support for process substitution via named pipes on systems without /dev/fd was only added to ksh in ksh93u+ in 2012. The public domain clone of ksh doesn't support it.

To my knowledge, no other Bourne-like shell supports it (rc, es, fish, non-Bourne-like shells support it but with a different syntax). yash has a <(...) construct, but that's for process redirection.

While quite useful, the feature was never standardized by POSIX. So, one can't expect to find it in sh, so shouldn't use it in a sh script.

Though the behaviour for <(...) is unspecified in POSIX, (so there would be no harm in retaining it), bash disables the feature when called as sh or when called with POSIXLY_CORRECT=1 in its environment.

So, if you have a script that uses <(...), you should use a shell that supports the feature to interpret it like zsh, bash or AT&T ksh (of course, you need to make sure the rest of the syntax of script is also compatible with that shell).

In any case:

cat <(cmd)

Can be written:

cmd | cat

Or just

cmd

For a command other than cat (that needs to be passed data via a file given as argument), on systems with /dev/fd/x, you can always do:

something | that-cmd /dev/stdin

Or if you need that-cmd's stdin to be preserved:

{ something 3<&- | that-cmd /dev/fd/4 4<&0 <&3 3<&-; } 3<&0 
  • Thank you for the detailed explanation. I have a few follow up questions/remarks. My cat/echo example is just that - a base example. My actual use case involves a python script that takes in 3 inputs, one of which is being passed at the moment as a <(...). This py script is being used within a shell script, and if I run the shell script using just its name (in which case it uses the bash interpreter), it works fine. – Ram RS May 10 '17 at 0:27
  • Now, when I use sh script.sh, it uses bash too, because that's the default shell on the HPC that I use. Is there any way I can ensure the script is invoked only using bash and not anything else? As in, can I check within the script if the script was invoked using bash script.sh or sh script.sh? – Ram RS May 10 '17 at 0:29
  • @Ram, see What is the portable (POSIX) way to achieve process substitution? for instance. You'd want to stop giving your script a sh extension if it's not a sh script, and use a shebang with the correct interpreter like #! /path/to/bash -. If you want to check that the shell is bash in your script, try [ -n "$BASH_VERSION" ], and whether it's in POSIX mode with [ -o posix ]. – Stéphane Chazelas May 10 '17 at 6:50
  • I found another of your answers very useful to my situation: unix.stackexchange.com/a/71137/135331 I'm going to see if I can incorporate that into my script. – Ram RS May 10 '17 at 12:39
  • Tried editing my previous comment, that didn't work. Anyway, it looks like which_interpreter won't work in my case. I'm going to have to package my scripts into Modulefiles and hope that the user doesn't bypass the proper usage, I guess. I could also error out on a POSIX shell, or change my script to be POSIX compliant. Hmmm. Thank you for your time, @stéphane-chazelas – Ram RS May 10 '17 at 12:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.