bash script/pgrep not working as expected - bash

I have a bash script that tries to call pgrep with arguments (Over simplified):
PATTERN="'/opt/apps/bin/lighttpd.bin -f /opt/apps/etc/lighttpd/lighttpd.conf\$'"
pgrep -f $PATTERN
echo pgrep -f $PATTERN
Gives the following output:
Usage: pgrep [-cflvx] [-d DELIM] [-n|-o] [-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]
[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] [PATTERN]
pgrep -f '/opt/apps/bin/lighttpd.bin -f /opt/apps/etc/lighttpd/lighttpd.conf$'
I suppose it means the argument is not passed to pgrep but is passed to echo for some reason.
What I'm expecting:
7632
pgrep -f '/opt/apps/bin/lighttpd.bin -f /opt/apps/etc/lighttpd/lighttpd.conf$'
When I run the preg line by itself, it outputs 7632 as expected.
Am I doing something wrong here? I've tried with sh, dash and bash. Same outcomes, I really don't see the problem.

You need to surround PATTERN in double quotes:
PATTERN="/opt/apps/bin/lighttpd.bin -f /opt/apps/etc/lighttpd/lighttpd.conf\$"
pgrep -f "$PATTERN"
See: quoting variables
Edit: and for echoing i would just do:
echo pgrep -f \'$PATTERN\'

As I don't have lighttpd.bin available to test with, I am submitting an untested option, mostly agreeing with #barti_ddu, but with a slightly different twist
PATTERN='/opt/apps/bin/lighttpd.bin -f /opt/apps/etc/lighttpd/lighttpd.conf\$'
pgrep -f "$PATTERN"
echo pgrep -f "$PATTERN"
I would keep the single quotes on the assingment to PATTERN, but totally agree you need the dbl-quoting when using with pgrep or echo.
I hope this helps.

Related

Why is my Bash script with grep not working?

I'm trying to understand why this very simple script is not working:
echo "SomeText" > test
if [[ "grep 'FindMe' ./test" ]] ; then
echo Why is this line written to screen?
fi
I have tried with:
"grep 'FindMe' ./test"
$(grep 'FindMe' ./test)
`grep 'FindMe' ./test`
also with a lot of grep options.
I have tried with [] and [[]] and {} and any combination.
In fact, to check the exit status, use no parentheses and no quotes:
if grep 'FindMe' ./test ; then
Using grep -q is common in such situations as we don't want the output of the script to be cluttered with random matches.

Bash - Is there a way to replace a bash command with parameters every time is typed with another parameter?

I am working on a HPC running slurm and CentOS. My workflow software (Nextflow v19.10.0) needs to execute this command
squeue --noheader -o %i %t -t all -u username
However, I have an error raises the following error
squeue: error: Unrecognized option: %
Usage: squeue [-A account] [--clusters names] [-i seconds] [--job jobid] [-n name] [-o format] [-p partitions] [--qos qos] [--reservation reservation] [--sort fields] [--start] [--step step_id] [-t states] [-u user_name] [--usage] [-L licenses] [-w nodes] [--federation] [--local] [--sibling] [-ahjlrsv]
Is there a way to wrap the above command in my .bashrc file, so when ever Nextflow runs the command it would automatically turned into this, which I have tested to work on my cluster?
squeue --noheader -o "%i %t" -t all -u username
Thanks so much for your help!!!
If Nextflow is running bash (the shell you tagged this question for), not /bin/sh (which is more common, as it's what the system() library call in many languages invokes), you can do this in any enclosing shell:
# override *any* call to squeue with a very specific command that's known to work
squeue() {
printf 'Ignoring old squeue arguments: ' >&2
printf '%q ' "$#" >&2
printf '\n' >&2
command squeue --noheader -o '%i %t' -t all -u username
}
export -f squeue
However, that probably won't work: It's likely that Nextflow is actually using sh instead, so instead of using an exported function, you'll want to create a directory with a squeue executable script in it that then invokes the real squeue command. Thus:
#!/bin/bash
printf 'Ignoring old squeue arguments: ' >&2
printf '%q ' "$#" >&2
printf '\n' >&2
# FIXME: replace /usr/bin/squeue with the actual location of the real command
exec /usr/bin/squeue --noheader -o '%i %t' -t all -u username

GNU Parallel: Do not quote Perl replacement string values that contain spaces?

The script I am writing with parallel currently looks like this:
#!/bin/bash
seq ${2:-3} | parallel --tty -j0 sidplayfp -wch{}.wav '{=$_=join" ",map{"-u".$_}grep!/#{[seq()]}/,(1..total_jobs())=}' ${#:3} -q $1 '2>/dev/null'
For example, running ./sidrender.sh Stomp.sid is expected to execute the following commands:
sidplayfp -wch1.wav -u2 -u3 -q Stomp.sid 2>/dev/null
sidplayfp -wch2.wav -u1 -u3 -q Stomp.sid 2>/dev/null
sidplayfp -wch3.wav -u1 -u2 -q Stomp.sid 2>/dev/null
However, this does not work properly, and when looking with --dry-run, it turns out that parallel is quoting together the -u flags (e.g. sidplayfp -wch1.wav '-u2 -u3' -q Stomp.sid 2>/dev/null) because they come from the same Perl expression.
Here's a minimal example of what is happening:
$ parallel --dry-run 'echo {= $_="foo bar" =}' ::: 1
echo 'foo bar'
If you were to replace "foo bar" with "foo", the output would be echo foo without any quotes.
Because sidplayfp does not properly parse the arguments when they are quoted, I need a way to stop parallel from quoting the output, but I cannot find a way to do so in the man page.
Use eval:
seq ${2:-3} |
parallel --tty -j0 eval sidplayfp -wch{}.wav '{=$_=join" ",map{"-u".$_}grep!/#{[seq()]}/,(1..total_jobs())=}' ${#:3} -q $1 '2>/dev/null'

Using pkill, how can I find out how many processes matched

We may not know how many processes actually died as a result of a pkill command, but is there a way to echo how many processes matched?
pkill -f "bin/ql_node_server"
is there a switch to pkill that will echo how many processes matched?
on MacOS, if I use the -c option with pkill, I get:
pkill: illegal option -- c
usage: pkill [-signal] [-ILfilnovx] [-F pidfile] [-G gid]
[-P ppid] [-U uid] [-g pgrp]
[-t tty] [-u euid] pattern ...
You can use -c flag.
From man pkill:
-c, --count
Suppress normal output;
instead print a count of matching processes.
When count does not match anything, e.g. returns zero, the command will return non-zero value.

Pass command via variable in shell

I have following code in my build script:
if [ -z "$1" ]; then
make -j10 $1 2>&1 | tee log.txt && notify-send -u critical -t 7 "BUILD DONE"
else
make -j10 $1 2>&1 | tee log.txt | grep -i --color "Error" && notify-send -u critical -t 7 "BUILD DONE"
fi
I tried to optimize it to:
local GREP=""
[[ ! -z "$1" ]] && GREP="| grep -i --color Error" && echo "Grepping for ERRORS"
make -j10 $1 2>&1 | tee log.txt "$GREP" && notify-send -u critical -t 7 "BUILD DONE"
But error thrown in make line if $1 isn't empty. I just can't figure out how to pass command with grep pipe through the variable.
Like others have already pointed out, you cannot, in general, expect a command in a variable to work. This is a FAQ.
What you can do is execute commands conditionally. Like this, for example:
( make -j10 $1 2>&1 && notify-send -u critical -t 7 "BUILD DONE" ) |
tee log.txt |
if [ -z "$1" ]; then
grep -i --color "Error"
else
cat
fi
This has the additional unexpected benefit that the notify-send is actually conditioned on the exit code of make (which is probably what you intended) rather than tee (which I would expect to succeed unless you run out of disk or something).
(Or if you want the notification regardless of the success status, change && to just ; -- I think this probably makes more sense.)
This is one of those rare Useful Uses of cat (although I still feel the urge to try to get rid of it!)
You can't put pipes in command variables:
$ foo='| cat'
$ echo bar $foo
bar | cat
The linked article explains how to do such things very well.
As mentioned in #l0b0's answer, the | will not be interpreted as you are hoping.
If you wanted to cut down on repetition, you could do something like this:
if [ $(make -j10 "$1" 2>&1 > log.txt) ]; then
[ "$1" ] && grep -i --color "error" log.txt
notify-send -u critical -t 7 "BUILD DONE"
fi
The inside of the test is common to both branches. Instead of using tee so that the output can be piped, you can just indirect the output to log.txt. If "$1" isn't empty, grep for any errors in log.txt. Either way, do the notify-send.

Resources