How to use nohup with curly braces? - bash

I try to run the following command (ref.) using nohup, which basically separates stdout and stderr into two processes.
{ foo 2>&1 1>&3 3>&- | sed -u 's/^/err: /'; } 3>&1 1>&2 | sed -u 's/^/out: /'
The foo script is like below.
#!/bin/bash
while true; do
echo a
echo b >&2
sleep 1
done
This is the test result.
$ nohup { foo 2>&1 1>&3 3>&- | sed -u 's/^/err: /'; } 3>&1 1>&2 | sed -u 's/^/out: /' >/dev/null 2>&1 &
-bash: syntax error near unexpected token `}'

That's syntatically impossible. But you can wrap your {} in a sh -c cmd:
nohup sh -c 'foo 2>&1 1>&3 3>&- | sed -u "s/^/err: /"'
Notice I change the single quote for sed to double quote.

Related

How to hide ls -l output in bash script when used with time command?

I am writing a bash script to store the individual values of minutes and seconds.
However, I am unable to do so.
TIMEFORMAT='%3lR'
exec 3>&1 4>&2
cmd='ls -l / >/dev/null 2>&1'
var=$( { time $cmd 1>&3 2>&4; } 2>&1 ) # Captures time only.
exec 3>&- 4>&-
echo 'Get the var'
echo $var
min=$(echo $var | awk -F 'm|s' '{print $2}')
echo $min
The output shows ls -l / which I don't want.
Next, I don't understand what is exec 3>&- 4>&-. I copied this from other stack overflow answers. I don't understand what is the use of { inside the ( bracket.
Try this:
TIMEFORMAT='%3lR'
var=$({ time ls -l / 2>&1 > /dev/null; } 2>&1)
echo 'Get the var'
echo ${var}
min=$(echo ${var} | awk -F 'm|s' '{print $2}')
echo ${min}

Use multiple tee commands redirections in a watch call

The following command processes the output of the pipe twice by using tee:
echo -e "ALPHA\nBRAVO" | tee >(head -n 1) >(tail) >/dev/null
As expected it outputs:
ALPHA
ALPHA
BRAVO
When trying to call it with watch like this:
watch 'echo -e "ALPHA\nBRAVO" | tee >(head -n 1) >(tail) >/dev/null'
It returns:
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `echo -e "ALPHA\nBRAVO" | tee >(head -n 1) >(tail) >/dev/null'
How should I escape my command to use it with watch?
Process substitutions are an extension, not all sh implementations support them. You can use redirections to circumvent this restriction though. Like
watch '{ { printf '\''ALPHA\nBRAVO\n'\'' |
tee /proc/self/fd/3 |
head -n 1 >&4
} 3>&1 | tail >&4
} 4>&1'
Just note that this is no more portable than doing watch 'bash -c ...'.

Pipe stdout and stderr through ssh

Consider the following example:
{ echo 1 | tee /dev/stderr 2> >(sed -e 's|1|err|' >&2) 1> >(sed -e 's|1|out|') ; }
which prints
out
err
Note that out out is printed on stdout and err on stderr.
Question: How to do this remotely via ssh?
More precisely, how to run
ssh host 'echo 1 | tee /dev/stderr SOME_MAGIC_HERE'
st. again out/err pops up on stdout/stderr (for an appropriate bash magic SOME_MAGIC_HERE).
Clearly, the following works:
ssh host 'echo 1 | tee /dev/stderr' 2> >(sed -e 's|1|err|' >&2) 1> >(sed -e 's|1|out|')
But that executes sed locally, and I'd rather want to do that remotely on host.
after the update:
ssh host 'echo 1 | tee >(cat - | sed -e "s|1|err|" >&2) | sed -e "s|1|out|"'
out
err
The idea is to use <pipes> | for processing /dev/stdout and use process substitution in combination with tee to create the /dev/stderr part.
Now it works as expected:
$ ssh host 'echo 1 | tee >(cat - | sed -e "s|1|err|" >&2) | sed -e "s|1|out|"' > /dev/null
err
$ ssh host 'echo 1 | tee >(cat - | sed -e "s|1|err|" >&2) | sed -e "s|1|out|"' 2> /dev/null
out
original answer:
The following command executes by changing your <single quotes> into <double quotes> :
ssh host 'echo 1 | tee /dev/stderr 2> >(sed -e "s|1|err|") 1> >(sed -e "s|1|out|")'
but this has everything in /dev/stdout. Example:
$ ssh host 'echo 1 | tee /dev/stderr 2> >(sed -e "s|1|err|") 1> >(sed -e "s|1|out|")' > /dev/null
$ ssh host 'echo 1 | tee /dev/stderr 2> >(sed -e "s|1|err|") 1> >(sed -e "s|1|out|")' 2> /dev/null
out
err
and this is exactly what your original command does on the host system:
{ echo 1 | tee /dev/stderr 2> >(sed -e "s|1|err|") 1> >(sed -e "s|1|out|") ; } >/dev/null
{ echo 1 | tee /dev/stderr 2> >(sed -e "s|1|err|") 1> >(sed -e "s|1|out|") ; } 2>/dev/null
out
err
The ssh program normally handles the passing of /dev/stdout,/dev/stderr and /dev/stdin correctly:
$ ssh host "echo 1; echo 2 > /dev/stderr" > /dev/null
2
$ ssh host "echo 1; echo 2 > /dev/stderr" 2> /dev/null
1

Bash - redirect stdout to log and screen with stderr only to log

I would like to do the following;
Redirect a copy of stdout to logfile and keep stdout on the screen.
Redirect stderr to the same logfile and not display on the screen.
Code without stdout to screen:
#!/bin/bash
exec 1> >(sed -u 's/^/INF: /' >> common.log)
exec 2> >(sed -u 's/^/ERR: /' >> common.log)
echo "some txt"
echo "an error" >&2
echo "some more txt"
echo "one more error" >&2
Log:
INF: some txt
INF: some more txt
ERR: an error
ERR: one more error
The first issue is buffering which I tried to negate with sed '-u' for unbuffered.
Code with stdout to screen:
#!/bin/bash
exec 1> >(sed -u 's/^/INF: /' | tee -a common.log)
exec 2> >(sed -u 's/^/ERR: /' >> common.log)
echo "some txt"
echo "an error" >&2
echo "some more txt"
echo "one more error" >&2
Results in the screen hanging (had to Ctrl-C) and log still buffered. Suggestions?
Does this work for you?
command 2> >(sed -u 's/^/ERR: /' >> common.log) | sed -u 's/^/INF: /' | tee -a common.log
Where command is your command.

tee and pipelines inside a bash script

i need to redirect stout and stderr in bash each to separate file.
well i completed this command:
((/usr/bin/java -jar /opt/SEOC2/seoc2.jar 2>&1 1>&3 | tee --append /opt/SEOC2/log/err.log) 3>&1 1>&2 | tee --append /opt/SEOC2/log/app.log) >> /opt/SEOC2/log/combined.log 2>&1 &
which works fine running from a command line.
trying to put the very same command into bash script
...
12 cmd="(($run -jar $cmd 2>&1 1>&3 | tee --append $err) 3>&1 1>&2 | tee --append $log) >> $combined 2>&1"
...
30 echo -e "Starting servis..."
31 $cmd &
32 pid=`ps -eo pid,args | grep seoc2.jar | grep -v grep | cut -c1-6`
33 if [ ! -z $pid ]; then
...
leads to error like this:
root#operator:/opt/SEOC2# seoc2 start
Starting servis...
/usr/local/bin/seoc2: line 31: ((/usr/bin/java: dir or file doesn't exist
tried to cover this command by $( ), ` ` etc but with no effect at all :(
any suggestion or advice would be very appreciated, playing around for hours already :/
thanx a lot
Rene
If you store the whole command line in a variable you have to use eval to execute it:
cmd="(($run -jar $cmd 2>&1 1>&3 | tee --append $err) 3>&1 1>&2 | tee --append $log) >> $combined 2>&1"
...
eval $cmd &

Resources