Why do I get errors when using > > in bash? - bash

I've been looking up some bash stuff today and a few snippets I've tried have included > > which seems to be the cause of the errors I'm receiving.
Example snippet:
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
Here's what I'm working with.
$ bash --help
GNU bash, version 3.2.48(1)-release-(x86_64-apple-darwin12)
EDIT
Here are the errors I'm gettting:
file.sh: line 14: syntax error near unexpected token `>'
file.sh: line 14: `command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)'

According to the answers to this question, Bash 3.2.48 for Mac OS X has some limitations when it comes to process-substitution; it supports it in some cases but not others. Your case is apparently one where it doesn't.
Quoting from the best/most-helpful answer there:
The one from macports (4.2.37 — normally /opt/local/bin/bash if you have it installed) works fine. […] Perhaps you may want to use macports bash for this script.

The most naive way to solve your problem is the following:
while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done < <( { command > /dev/null ; } 2>&1 )
We're sending all stdout of command to /dev/null and then then redirecting the stderr to stdin.
Or the other way round:
{ command > /dev/null ; } 2>&1 | while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done
With these methods you don't have to play too much with file descriptors!
Done.

Related

sh: syntax error near unexpected token `<'

I am creating a script where I am redirecting the output of an awk command into a loop, but it's giving me this error:
sh: -c: line 3: syntax error near unexpected token `<'
sh: -c: line 3: `done < <(awk "{print}" inputfile.txt)'
I was able to run it against a different remote host, but this host is giving me an error. Does anyone know if some versions of sh/bash don't support that syntax, or know any alternatives to that syntax, or maybe spot a bug that I haven't been able to see? (I'm new to bash scripting, so even a point in the right direction would be helpful!)
Here is a pared-down version of my script that I was able to reproduce the issue on:
#!/usr/bin/env bash
host=$1
ssh $host 'while read line
do
echo "hi";
done < <(awk "{print}" inputfile.txt)'
It looks like the remote user on that host uses a shell that's not Bash to run the command, see this Q&A; a way around that is to either avoid Bashisms:
ssh "$host" 'awk "{print}" inputfile.txt \
| while IFS= read -r line; do
echo "$line"
done'
which comes with its own pitfalls, see A variable modified inside a while loop is not remembered – alternatively, you could specify that the remote host should run Bash:
ssh "$host" <<'EOF'
bash -c 'while IFS= read -r line; do
echo "$line"
done < <(awk "{print}" inputfile.txt)'
EOF

Bash script command and getting rid of shellcheck error SC2181

I have the following bash script:
dpkg-query --show --showformat='${Status}\n' "$i" 2> \
/dev/null | grep "install ok installed" &> /dev/null
if [[ $? -eq 0 ]]; then
l_var_is_desktop="true"
fi
and the ShellCheck utility (https://www.shellcheck.net/) is giving me the following output:
$ shellcheck myscript
Line 17:
if [[ $? -eq 0 ]]; then
^-- SC2181: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.
$
The link to this warning is the following: https://github.com/koalaman/shellcheck/wiki/SC2181
What is the best way for modifying this. The command is really too long to put into one line. I would like to avoid using ShellCheck ignore rules.
I've tried creating a local variable and storing the output of the command, but this breaks other rules.
The command doesn't really get much longer by putting it directly in if, you're just adding 3 characters.
if dpkg-query --show --showformat='${Status}\n' "$i" 2> \
/dev/null | grep "install ok installed" &> /dev/null
then
l_var_is_desktop="true"
fi

Redirect stdout and stderr after redirection command

In a bash script I am enabling IP forwarding using the following command:
echo 1 > /proc/sys/net/ipv4/ip_forward
However I also want to include my own error messages so I have to redirect the stdout and stderr to /dev/null, which looks like the following:
echo 1 > /proc/sys/net/ipv4/ip_forward >/dev/null 2>&1
This works for commands that do not have the redirection symbol in it, for example:
route add default gw 10.8.0.1 > /dev/null 2>&1
Is there any way I can make this work for commands that do have a redirection in them? Is there a workaround for this? Any other way I can do this better?
Your redirections are nonsensical (no offense).
This:
echo 1 > /proc/sys/net/ipv4/ip_forward >/dev/null 2>&1
will:
redirect echo's standard output to /proc/sys/net/ipv4/ip_forward;
redirect echo's standard output to /dev/null
redirect echo's standard error to where file descriptor 1 points, i.e., to /dev/null.
Hence, globally, redirection 2 cancels redirection 1: nothing is going to go to /proc/sys/net/ipv4/ip_forward!
I guess you want to redirect echo's standard output to /proc/sys/net/ipv4/ip_forward and echo's standard error to /dev/null. This is achieved by:
echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null
But read on, I believe that's not the answer you're looking for!
Why do you want to redirect echo's standard error to /dev/null?
echo very rarely writes to standard error. In fact, the only time echo will write to standard error is when there's a write error. There are a couple of ways this could happen:
if the disk is full (that can be simulated with /dev/full):
$ echo hello >/dev/full
bash: echo: write error: No space left on device
$ echo hello >/dev/full 2>/dev/null
$
(no error messages shown with the redirection 2>/dev/null).
if echo's standard out is closed:
$ ( exec >&-; echo hello )
bash: echo: write error: Bad file descriptor
$ ( exec >&-; echo hello 2> /dev/null )
$
(no error messages shown with the redirection 2>/dev/null).
There might be other cases where echo outputs to standard error. But the following are certainly not among them:
Redirecting echo's standard output to a non-existent file descriptor:
$ echo hello >&42
bash: 42: Bad file descriptor
$ echo hello >&42 2>/dev/null
bash: 42: Bad file descriptor
$
The redirection 2>/dev/null doesn't fix anything; you can actually see that the error comes from bash and not from echo (the latter would have bash: echo: as a prefix).
Redirecting echo's standard output to a file without write permission:
$ touch testfile
$ chmod -w testfile
$ echo hello > testfile
bash: testfile: Permission denied
$ echo hello > testfile 2>/dev/null
bash: testfile: Permission denied
$
Same as above, the redirection 2>/dev/null doesn't fix anything.
The previous cases are not fixed by 2>/dev/null, since the error occurs at Bash's level, before the command is even executed and the redirections performed, because it's at the moment of the redirection that Bash encounters an error: it can't open the stream for writing and outputs the error message to standard output.†
Now I guess you're trying to fix the following scenario: when the user doesn't have enough rights to write to /proc/sys/net/ipv4/ip_forward:
$ echo 1 >/proc/sys/net/ipv4/ip_forward
bash: /proc/sys/net/ipv4/ip_forward: Permission denied
$
The error message on standard error can't be redirected with a simple redirection‡:
$ echo 1 >/proc/sys/net/ipv4/ip_forward 2>/dev/null
bash: /proc/sys/net/ipv4/ip_forward: Permission denied
$
A standard way to redirect the error that occurs at the redirection level (i.e., before the command is even executed) is to use groupings:
$ { echo 1 >/proc/sys/net/ipv4/ip_forward; } 2>/dev/null
Now that explains why the solution you posted as an answer works: let's go through it and we'll see that there's something that shows you didn't completely understand redirections (and hopefully this post will help you understanding a few things); your code is:
function ip_forward
{
echo 1 > /proc/sys/net/ipv4/ip_forward
}
ip_forward >/dev/null 2>&1
This will run the function ip_forward, and redirect:
its standard output to /dev/null;
and then its standard error to where its standard output points (i.e., to /dev/null).
But the function ip_forward doesn't output anything to standard output! so the redirection >/dev/null is only useful for the 2>&1 part of the redirection. In fact, your code is completely equivalent to:
function ip_forward
{
echo 1 > /proc/sys/net/ipv4/ip_forward
}
ip_forward 2>/dev/null
But then (since you only use a function construct as a way to achieve what you wanted—not because you want a function), it's much better to write your code as either:
echo 1 2>/dev/null >/proc/sys/net/ipv4/ip_forward
or
{ echo 1 > /proc/sys/net/ipv4/ip_forward; } 2>/dev/null
(the latter being preferred).
Sorry for this long post!
†
There's something we should be aware of: the order of redirection. They are performed from left to right, as Bash reads them. How about we first redirect standard error, and then standard output to a non-existent/non-writable stream?
$ echo hello 2>/dev/null >&42
$
That's right, it works.
‡
Well, can, if you understood the previous footnote:
$ echo 1 2>/dev/null >/proc/sys/net/ipv4/ip_forward
$ echo $?
1
$
No error on standard error! that's because of the order of the redirections.
Solved
echo 1 > /proc/sys/net/ipv4/ip_forward ip_forward 2>/dev/null
echo 1 is the stdout part so this does not have to be redirected anymore. I only had to redirect stderr by adding 2>/dev/null.

Fails to read lines from running process in bash

Using process substitution, we can get every lines of output of a command .
# Echoes every seconds using process substitution
while read line; do
echo $line
done < <(for i in $(seq 1 10); do echo $i && sleep 1; done)
By the same way above, I want to get the stdout output of 'wpa_supplicant' command, while discarding stderr.
But nothing can be seen on screen!
while read line; do
echo $line
done < <(wpa_supplicant -Dwext -iwlan1 -c${MY_CONFIG_FILE} 2> /dev/null)
I confirmed that typing the same command in prompt shows its output normaly.
$ wpa_supplicant -Dwext -iwlan1 -c${MY_CONFIG_FILE} 2> /dev/null
What is the mistake? Any help would be appreciated.
Finally I found the answer here!
The problem was easy... the buffering. Using stdbuf (and piping), the original code will be modified as below.
stdbuf -oL wpa_supplicant -iwlan1 -Dwext -c${MY_CONFIG_FILE} | while read line; do
echo "! $line"
done
'stdbuf -oL' make the stream line buffered, so I can get every each line from the running process.

Read full stdin until EOF when stdin comes from `cat` bash

I'm trying to read full stdin into a variable :
script.sh
#/bin/bash
input=""
while read line
do
echo "$line"
input="$input""\n""$line"
done < /dev/stdin
echo "$input" > /tmp/test
When I run ls | ./script.sh or mostly any other commands, it works fine.
However It doesn't work when I run cat | ./script.sh , enter my message, and then hit Ctrl-C to exit cat.
Any ideas ?
I would stick to the one-liner
input=$(cat)
Of course, Ctrl-D should be used to signal end-of-file.

Resources