Problem with temporary unnamed pipe in bash script - bash

I have the following question:
When I execute the following script directly in a terminal window, the commands behave as expected.
$ diff <(echo tmp) <(echo tmp1)
1c1
< tmp
---
> tmp1
However when I write the same command in a shell script
#! /bin/bash
diff <(echo tmp) <(echo tmp1)
I get the following error message:
$ sh test.sh
test.sh: line 2: syntax error near unexpected token `('
test.sh: line 2: ` diff <(echo tmp) <(echo tmp1)'
Initially I thought this was an issue with diff, but this also happens with other commands. Does anybody have an idea what causes the problem?

Try
bash test.sh
or
chmod ugo+x test.sh
./test.sh
Works fine for me when I do either.
Looks like the syntax is not supported by the bourne shell (sh).

When bash is invoked using sh, it starts up in a special, POSIX-compliant mode. This has different syntax, which I guess explains the different results.
See bashref of POSIX mode, #22: "process substitution is not available".

That syntax doesn't look familiar. Are you sure you are using bash in your terminal? You can verify by typing echo $SHELL.

Related

awk and bash script? [duplicate]

This question already has answers here:
Syntax error in shell script with process substitution
(4 answers)
Closed 3 years ago.
I wonder why it doesn't work.
Please advise me.
1. working
$ nu=`awk '/^Mem/ {printf($2*0.7);}' <(free -m)`
$ echo $nu
1291.5
2. not working
$ cat test.sh
#!/bin/bash
nu=`awk '/^Mem/ {printf($2*0.7);}' <(free -m)`
echo $nu
$ sh test.sh
test.sh: command substitution: line 2: syntax error near unexpected token `('
test.sh: command substitution: line 2: `awk '/^Mem/ {printf($2*0.7);}' <(free -m)'
Could you please try following.
nu=$(free -m | awk '/^Mem/ {print $2*0.7}')
echo "$nu"
Things taken care are:
Use of backtick is depreciated so use $ to store variable's value.
Also first run free command pass its standard output as standard input to awk command by using |(which should be ideal way of sending output of a command to awk in this scenario specially) and save its output to a variable named nu.
Now finally print variable nu by echo.
Since <(...) process substitution is supported by bash not by sh so I am trying to give a solution where it could support without process substitution (which I mentioned a bit earlier too).
The <( ) construct ("process substitution") is not available in all shells, or even in bash when it's invoked with the name "sh". When you run the script with sh test.sh, that overrides the shebang (which specifies bash), so that feature is not available. You need to either run the script explicitly with bash, or (better) just run it as ./test.sh and let the shebang line do its job.
The reason to add a shebang in a script is to define an interpreter directive if the file has execution permission.
Then, you should invoke it by, for example
$ ./test.sh
once you have set the permission
$ chmod +x test.sh

Bash script using COMM and SORT issues syntax error near unexpected token

Linux, CentOS - to compare 2 files I use command
comm --check-order -1 --output-delimiter=----- <sort(file1.txt) <sort (file3.txt) > result.txt ;
and it works on shell, but when I try create a bash file - I have
syntax error near unexpected token `('
The script is simplest
#!/bin/bash
cd /var/www/html/compare/ ;
comm --check-order -1 --output-delimiter=----- <sort(file1.txt) <sort (file3.txt) > result.txt ;
exit ;
sh
I already tried variations with escaping of round brackets like
sort\(file1.txt\)
or
sort'(file1.txt)'
but this case shell says
sort(file1.txt)...: No such file or directory
I tried with absolute path like
<sort\(var/www/html/compare/file1.txt\)
same result "No such file"
and I already tried run the script with variations like
sh /a/compare.sh
bash /a/compare.sh
chmod +x /a/compare.sh; ./a/compare.sh
Still same problem.
So I have OR "No such file..." with escaping of brackets - OR "unexpected token"in other cases.
Thanks in advance for any ideas to try, may be should be a right "mix" of syntax ?
After many combinations I found the solution where we need to force BASH with a little specific escaping.
This script works
#!/bin/bash
cd /var/www/html/compare/ ;
`bash -c "comm --check-order -1 --output-delimiter=----- <(sort file1.txt) <(sort file2.txt) > result.txt" ` ;
exit ;
sh
Hopefully will help somebody to save the time.
!/usr/bin/env bash
to solve it, instead of label the command, label the file
Process substitution puts the entire command in parens.
... <(sort ...) ...

Is it possible to mimic process substitution on msys /mingw (with bash 3.x)

I am trying to use process substitution to avoid using temporary files. I tried the following:
diff <(echo "a") <(echo "b")
on mingw32/msys (from http://www.mingw.org/ as of Dec 2013), and got:
sh: syntax error near unexpected token `('
Running the same thing on Ubuntu 12.04 returns:
1c1
< a
---
> b
The msys I use probably has bash 3.1. I was wondering if it is possible to work around the issue so that the same job can be done in msys/older bash without using temporary files.
Thanks.
I mananed to do process substitution in bash 3.x. The syntax is correct. It is supported by bash shell. So I would suggest to check what shell your are running and execute somiting like:
/bin/bash diff <(echo "a") <(echo "b")
Process substitution is not a POSIX compliant feature. To enable try to run:
set +o posix
See also this for more info.
The other way is to use named pipes. I tested on GNU bash version 4.1.2(1):
diff - p <<< "test" & echo "test2" > p
See Working with Named Pipes

Why will script work with /bin/bash but not /bin/sh?

I am trying to understand why the script will work with #!/bin/bash but not #!/bin/sh. I am running Cygwin and both sh.exe and bash.exe seem to be identical (same file size).
$ cat 1.sh
#!/bin/sh
while read line; do
echo ${line:0:9}
done < <(help | head -5)
$ ./1.sh
./1.sh: line 4: syntax error near unexpected token `<'
./1.sh: line 4: `done < <(help | head -5)'
$ cat 2.sh
#!/bin/bash
while read line; do
echo ${line:0:9}
done < <(help | head -5)
$ ./2.sh
GNU bash,
These she
Type `hel
Use `info
Use `man
Despite being same file, shell analyzes its own name when run and switches to either plain shell or bash mode.
The Problems
Bash is a superset of the Bourne shell, so many things are possible in Bash that aren't possible in more limited shells.
Even when sh is a hardlink to bash, it behaves differently when invokes as sh. Many features supported by the Bash shell will not work in this mode.
The Solutions
Remove your bashisms from the script, including nifty features like the process substitution you have on line 4.
Run your script as Bash, not vanilla Bourne.

"< <(command-here)" shell idiom resulting in "redirection unexpected"

This command works fine:
$ bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
However, I don't understand how exactly stable is passed as a parameter to the shell script that is downloaded by curl. That's the reason why I fail to achieve the same functionality from within my own shell script - it gives me ./foo.sh: 2: Syntax error: redirection unexpected:
$ cat foo.sh
#!/bin/sh
bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
So, the questions are: how exactly this stable param gets to the script, why are there two redirects in this command, and how do I change this command to make it work inside my script?
Regarding the "redirection unexpected" error:
That's not related to stable, it's related to your script using /bin/sh, not bash. The <() syntax is unavailable in POSIX shells, which includes bash when invoked as /bin/sh (in which case it turns off nonstandard functionality for compatibility reasons).
Make your shebang line #!/bin/bash.
Understanding the < <() idiom:
To be clear about what's going on -- <() is replaced with a filename which refers to the output of the command which it runs; on Linux, this is typically a /dev/fd/## type filename. Running < <(command), then, is taking that file and directing it to your stdin... which is pretty close the behavior of a pipe.
To understand why this idiom is useful, compare this:
read foo < <(echo "bar")
echo "$foo"
to this:
echo "bar" | read foo
echo "$foo"
The former works, because the read is executed by the same shell that later echoes the result. The latter does not, because the read is run in a subshell that was created just to set up the pipeline and then destroyed, so the variable is no longer present for the subsequent echo.
Understanding bash -s stable:
bash -s indicates that the script to run will come in on stdin. All arguments, then, are fed to the script in the $# array ($1, $2, etc), so stable becomes $1 when the script fed in on stdin is run.

Resources