Doesn't sh support process substitution (...)?
The syntax <(...)
is only supported by BASH.
For any POSIX shell, use this approach:
sh -c 'tail -n 1000 -F catalina.out | if grep -qP --line-buffered ".+" ; then ...'
i.e. move the stdin redirection in front of the if
with a pipe. The if
will pass stdin on to the grep
.
if tail ...| grep
won't work since the if
won't be able to see it's then
/fi
because the pipe separates processes.
Why does process substitution not work in a shell script?
It isn't entirely clear yet, but the chances are very high that you either have an incorrect shebang line at the top of the script:
#!/bin/sh
or you are using sh script.sh
instead of bash script.sh
while testing it, or you have SHELL=/bin/sh
or something similar set in the environment. Your failure is on the process substitution code. When Bash is run as sh
(in POSIX mode), then process substitution is not available:
- Process substitution is not available.
You need to write:
#!/bin/bash
temp=$(comm -12 <(sort -u /home/xyz/a.csv1) <(sort -u /home/abc/tempfile) | wc -l)
echo $temp
or even simply:
#!/bin/bash
comm -12 <(sort -u /home/xyz/a.csv1) <(sort -u /home/abc/tempfile) | wc -l
which will achieve the same effect as the capture followed by the echo. When testing, use bash -x script.sh
or bash script.sh
.
Deciphering the indecipherable comment
In an indecipherable comment, the information appears to include:
BASH=/bin/sh
BASHOPTS=cmdhist:extquote:force_fignore:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=([0]="0")
BASH_SOURCE=([0]="a.sh")
BASH_VERSINFO=([0]="4" [1]="1" [2]="2" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")
BASH_VERSION='4.1.2(1)-release'
CVS_RSH=ssh
SHELL=/bin/bash
SHELLOPTS=braceexpand:hashall:interactive-comments:posix
SHLVL=2
Note that BASH=/bin/sh
and SHELLOPTS=braceexpand:hashall:interactive-comments:posix
. Either or both of these might be a major part of the problem.
why process substitution does not always work with while loop in bash?
Note: <filename
is not process substitution. It's a redirection. Process substitution has the format <(command)
.
Process substitution substitutes the name of a process for the <(...)
. Despite the use of the < symbol, it is not a redirect.
So when you say cat <(echo foo)
, bash creates a subprocess to run the echo
command, and substitutes the name of a pseudo-file which can be read to get the output of that command. The result of the substitution will be something like this:
cat /dev/fd/63
Note the absence of a redirect. (You can see this in action by typing echo <(echo foo)
.)
Like many utilities, cat
can be invoked with or without a command-line argument; if no file is specified, then it reads from stdin
. So cat file.txt
and cat < file.txt
are very similar.
But the while
command does not accept additional arguments. So
while read -r line; do echo "$line"; done < file.txt
is valid, but
while read -r line; do echo "$line"; done file.txt
is a syntax error.
Process substitution doesn't change that. So
while read -r line; do echo "$line"; done /dev/fd/63
is a syntax error, and consequently so is
while read -r line; do echo "$line"; done <(echo foo)
To specify the redirect from the process substitution, you need a redirect:
while read -r line; do echo "$line"; done < <(echo foo)
Note that there must be a space between the two < symbols to avoid confusion with the "here-doc" syntax, <<word
.
Syntax error in shell script with process substitution
The syntax you've used is a bash extension to the basic shell syntax, so you must take care to run your script with bash. (Ksh also has >(…)
process substitution but doesn't support it after a redirection. Zsh would be fine.)
Given the error message you're getting, you are running this script in bash, but in its POSIX compatibility mode, not in full bash mode. Take care to invoke your script with an explicit #!/bin/bash
line. #!/bin/sh
won't do, even if /bin/sh
is a symbolic link to bash, because bash runs in POSIX mode if it's invoked under the name sh
. Always invoke bash by name if you use bash features.
Also take care not to set the environment variable POSIXLY_CORRECT
or to pass the --posix
option on the command line if you want to use bash features.
Alternatively, don't use this bash-specific syntax; use a portable construct such as the one proposed by Stephane Rouberol.
In bash, is it generally better to use process substitution or pipelines
tl;dr: Use pipes, unless you have a convincing reason not to.
Piping and redirecting stdin from a process substitution is essentially the same thing: both will result in two processes connected by an anonymous pipe.
There are three practical differences:
1. Bash defaults to creating a fork for every stage in a pipeline.
Which is why you started looking into this in the first place:
#!/bin/bash
cat "$1" | while IFS= read -r last; do true; done
echo "Last line of $1 is $last"
This script won't work by default with a pipelines, because unlike ksh
and zsh
, bash
will fork a subshell for each stage.
If you set shopt -s lastpipe
in bash 4.2+, bash mimics the ksh
and zsh
behavior and works just fine.
2. Bash does not wait for process substitutions to finish.
POSIX only requires a shell to wait for the last process in a pipeline, but most shells including bash
will wait for all of them.
This makes a notable difference when you have a slow producer, like in a /dev/random
password generator:
tr -cd 'a-zA-Z0-9' < /dev/random | head -c 10 # Slow?
head -c 10 < <(tr -cd 'a-zA-Z0-9' < /dev/random) # Fast?
The first example will not benchmark favorably. Once head
is satisfied and exits, tr
will wait around for its next write()
call to discover that the pipe is broken.
Since bash waits for both head
and tr
to finish, it will appear seem slower.
In the procsub version, bash only waits for head
, and lets tr
finish in the background.
3. Bash does not currently optimize away forks for single simple commands in process substitutions.
If you invoke an external command like sleep 1
, then the Unix process model requires that bash forks and executes the command.
Since forks are expensive, bash optimizes the cases that it can. For example, the command:
bash -c 'sleep 1'
Would naively incur two forks: one to run bash, and one to run sleep
. However, bash can optimize it because there's no need for bash
to stay around after sleep
finishes, so it can instead just replace itself with sleep
(execve
with no fork
). This is very similar to tail call optimization.
( sleep 1 )
is similarly optimized, but <( sleep 1 )
is not. The source code does not offer a particular reason why, so it may just not have come up.
$ strace -f bash -c '/bin/true | /bin/true' 2>&1 | grep -c clone
2
$ strace -f bash -c '/bin/true < <(/bin/true)' 2>&1 | grep -c clone
3
Given the above you can create a benchmark favoring whichever position you want, but since the number of forks is generally much more relevant, pipes would be the best default.
And obviously, it doesn't hurt that pipes are the POSIX standard, canonical way of connecting stdin/stdout of two processes, and works equally well on all platforms.
POSIX shell equivalent to ()
mkfifo foo.fifo
## if your "commands" is multiple commands
# { commands ...; } >foo.fifo &
# otherwise, if it's just one
commands ... >foo.fifo &
something_else foo.fifo
is the closest available equivalent to
something_else <( commands ... )
sh -c and process substitution
sh
is often dash
not bash
(see man sh
).dash
doesn't do process substitution, only POSIX stuff.
You'll need to do:
bash -c 'cat <(echo "hello")'
ksh
& zsh
can do process substitution too.
With your example, you can simply do:
watch -n 1 'iptables -L INPUT; iptables -L FORWARD'
no need for an advanced shell or process subtitution.
shell module: Bash (process substitution) with ansible
EDIT: though this answers this the question I think mgsk's answer is a better answer since I agree that it's not the right way to go about it with Ansible.
This should fix your issue:
- name: install pip
shell: "python <(curl https://bootstrap.pypa.io/get-pip.py)" executable=/bin/bash
If you are wondering the difference between these two commands:
python <(curl https://bootstrap.pypa.io/get-pip.py)
python <$(curl https://bootstrap.pypa.io/get-pip.py)
The first one uses process substitution which is a bash feature which is why you cannot use it with /bin/sh as your shell. What it's doing is taking the output of the curl command (which is a python script), writing it to a temporary file and using that file as an argument to python which takes a python script as its first argument.
The second one is an ambiguous redirect because the python script that is generated from the curl is not a file
cat (echo 'hello') can run, but can't run in shell script mode
It looks like you are using Bash-specific syntax in your script, so you should change the shebang line to something like:
#!/bin/bash
and if you want to invoke the program like you were doing you should run:
bash -x test.sh
syntax error near unexpected token `'
You get the error because process substitution (the <(some command)
part) is not a standard feature (defined in POSIX) in sh
, which means it may work on some OS but may not in others or in the same OS with different configuration.
You clarified that you have #!/bin/bash
at the top of your script, but I guess you still run the script via sh foo.sh
, as such, #!/bin/bash
will be ignored and the script is interpreted by sh
.
I assume your default shell is bash
(run echo $SHELL
), so all problems are gone if you paste the script in terminal and execute.
==== UPDATE ====
Possible solution if my assumption is correct:
Leave #!/bin/bash
as it is, make your script an executable by chmod +x foo.sh
. Then run it directly by ./foo.sh
Related Topics
Merge PDF Files with Numerical Sort
How to Capture Network Packets Per Pid
How to Use Expect with Optional Prompts
Xdotool Commands Bound to Key Shortcuts Doesnot Work
Control Mouse by Writing to /Dev/Input/Mice
Run Bash Commands from Txt File
Why Do We Need a Swapper Task in Linux
How to Tell Cmake I Want My Project to Link Libraries Statically
Insmod Error: Inserting './Hello.Ko': -1 Invalid Module Format"
How to Grep '---' in Linux? Grep: Unrecognized Option '---'
Getting List of Network Devices Inside the Linux Kernel
How to Save Output of "Watch" to File
How to Fix Permission Denied for .Git/ Directory When Performing Git Push
Capturing Performance with Pcap VS Raw Socket
Cmd 2>&1 > Log VS Cmd > Log 2>&1
Using Putty to Scp from Windows to Linux
How to Launch a New Process That Is Not a Child of the Original Process