If statement inside command line - bash

I'm trying to create a shell script and the fact is, I want to change the output if the variable $output is filled. I was thinking about checking the variable with an if inside the command but I don't know if it's the correct syntax. Here is an exemple (of course that doesn't work):
ls -lisa | awk '$5 == own' own="$owner" | sort -k$column -n if [
$output ]; then print > out.txt fi
I don't know if it's going to work that way and if it's possible.

The exec built-in can change the default standard output for the rest of the running shell script. So, in this case, you would do:
if [ -n "$output" ]; then
exec >out.txt
fi
ls -lisa | awk '$5 == own' own="$owner" | sort -k$column
I'm not entirely sure what you're trying to do with the awk part, so this is just verbatim from your question.
Another option is to put the part of your script that you want to redirect into a function, and then call the function in one of two ways, redirecting the output. Example:
do_work() {
ls -lisa | awk '$5 == own' own="$owner" | sort -k$column
}
if [ -n "$output" ]; then
do_work >out.txt
else
do_work
fi

You can use the shell's "use default value" option (${variable:-default}, with /dev/stdout as the default) to do this:
ls -lisa | awk '$5 == own' own="$owner" | sort -k$column -n > "${output:-/dev/stdout}"

Related

grep match a concat of variable and string (dash) in piped input

(NOTE: this is a bash question, not k8s)
I have a working script which will fetch the name
admin-job-0
from a list of kubernetes cronjobs, of which there can be up to 32 ie. admin-job-0 -1, -2, -3 ... -31
Question: How do I grep "-$1$" ie a dash, the number, and no more, instead of just the number as I have below?
Bonus question: Is there any way to do what I'm doing below without the if/else logic regardless of whether there's an argument passed?
fetch-admin-job() {
if [[ -n $1 ]]; then
name=$(kubectl get cronjob | awk '/^admin-job.*/{print $1}' | grep $1 )
else
# get the first one (if any)
name=$(kubectl get cronjob | awk '/^admin-job.*/{print $1}')
fi
echo $name
}
#example:
fetch-admin-job 0
You can replace your function code with this:
fetch-admin-job() {
kubectl get cronjob |
awk -v n="$1" '!n || $1 == "admin-job-" n {print $1}'
}
Then invoke it as:
fetch-admin-job 0
fetch-admin-job 4
fetch-admin-job
We are using this condition in awk:
!n: will be true when you don't pass anything in first argument
||: OR
$1 == "admin-job-" n: Will be used to compare first column in output of kubectl command with first argument you pass. Note that this is equivalent of awk '/^admin-job/ ...' | grep "-$1$".
You don't need to use grep on an awk output as awk can handle that part as well.
If you pass to grep a double-hyphen (--), this signals the end of the option and a dash at the start of the pattern does not harm, i.e.
grep -- "$1"
or
grep -- "$1$"
or whatever you want to achieve.

In a bash pipe, take the output of the previous command as a variable to the next command (Eg. if statement)

I wanted to write a command to compare the hash of a file. I wrote the below single line command. Wanted to understand as to how I can take the output of the previous command as a variable for the current command, in a pipe.
Eg. below command I wanted to compare the output of 1st command "Calculated hash" to the original hash. In the last command, I wanted to refer to the output of the previous command. How do I do that in the if statement? (Instead of $0)
sha256sum abc.txt | awk '{print $1}' | if [ "$0" = "8237491082roieuwr0r9812734iur" ]; then
echo "match"
fi
Following your narrow request looks like:
sha256sum abc.txt |
awk '{print $1}' |
if [ "$(cat)" = "8237491082roieuwr0r9812734iur" ]; then echo "match"; fi
...as cat with no arguments reads the command's stdin, and in a pipeline, content generated from prior stages are streamed into their successors.
Alternately:
sha256sum abc.txt |
awk '{print $1}' |
if read -r line && [ "$line" = "8237491082roieuwr0r9812734iur" ]; then echo "match"; fi
...wherein we read only a single line from stdin instead of using cat. (To instead loop over all lines given on stdin, see BashFAQ #1).
However, I would strongly suggest writing this instead as:
if [ "$(sha256sum abc.txt | awk '{print $1}')" = "8237491082roieuwr0r9812734iur" ]; then
echo "match"
fi
...which, among other things, keeps your logic outside the pipeline, so your if statement can set variables that remain set after the pipeline exits. See BashFAQ #24 for more details on the problems inherent in running code in pipelines.
Consider using sha256sum's check mode. If you save the output of sha256sum to a file, you can check it with sha256sum -c.
$ echo foo > file
$ sha256sum file > hash.txt
$ cat hash.txt
b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c file
$ sha256sum -c hash.txt
file: OK
$ if sha256sum -c --quiet hash.txt; then echo "match"; fi
If you don't want to save the hashes to a file you could pass them in via a here-string:
if sha256sum -c --quiet <<< 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c file'; then
echo "match"
fi

How to filter an ordered list stored into a string

Is it possible in bash to filter out a part of a string with another given string ?
I have a fixed list of motifs defined in a string. The order IS important and I want to keep only the parts that are passed as a parameter ?
myDefaultList="s,t,a,c,k" #order is important
toRetains="k,t,c,u" #provided by the user, order is not enforced
retained=filter $myDefaultList $toRetains # code to filter
echo $retained # will print t,c,k"
I can write an ugly method that will use IFS, arrays and loops, but I wonder if there's a 'clever' way to do that, using built-in commands ?
here is another approach
tolines() { echo $1 | tr ',' '\n'; }
grep -f <(tolines "$toRetains") <(tolines "$myDefaultList") | paste -sd,
will print
t,c,k
assign to a variable as usual.
Since you mention in your comments that you are open to sed/awk , check also this with GNU awk:
$ echo "$a"
s,t,a,c,k
$ echo "$b"
k,t,c,u
$ awk -v RS=",|\n" 'NR==FNR{a[$1];next}$1 in a{printf("%s%s",$1,RT)}' <(echo "$b") <(echo "$a")
t,c,k
#!/bin/bash
myDefaultList="s,t,a,c,k"
toRetains="s,t,c,u"
IFS=","
for i in $myDefaultList
do
echo $toRetains | grep $i > /dev/null
if [ "$?" -eq "0" ]
then
retained=$retained" "$i
fi
done
echo $retained | sed -e 's/ /,/g' -e 's/,//1'
I have checked it running for me. Kindly check.

bash: sed: unexpected behavior: displays everything

I wrote what I thought was a quick script I could run on a bunch of machines. Instead it print what looks like might be directory contents in a recursive search:
version=$(mysql Varnish -B --skip-column-names -e "SELECT value FROM sys_param WHERE param='PatchLevel'" | sed -n 's/^.*\([0-9]\.[0-9]*\).*$/\1/p')
if [[ $(echo "if($version == 6.10) { print 1; } else { print 0; }" | bc) -eq 1 ]]; then
status=$(dpkg-query -l | awk '{print $2}' | grep 'sg-status-polling');
cons=$(dpkg-query -l | awk '{print $2}' | grep 'sg-consolidated-poller');
if [[ "$status" != "" && "$cons" != "" ]]; then
echo "about to change /var/www/Varnish/lib/Extra/SG/ObjectPoller2.pm"; echo;
cp /var/www/Varnish/lib/Extra/SG/ObjectPoller2.pm /var/www/Varnish/lib/Extra/SG/ObjectPoller2.pm.bkup;
sed -ir '184s!\x91\x93!\x91\x27--timeout=35\x27\x93!' /var/www/Varnish/lib/Extra/SG/ObjectPoller2.pm;
sed -n 183,185p /var/www/Varnish/lib/Extra/SG/ObjectPoller2.pm; echo;
else
echo "packages not found. Assumed to be not applicable";
fi
else
echo "This is 4.$version, skipping";
fi
The script is supposed to make sure Varnish is version 4.6.10 and has 2 custom .deb packages installed (not through apt-get). then makes a backup and edits a single line in a perl module from [] to ['--timeout=35']
it looks like its tripping up on the sed replace one liner.
There are two major problems (minor ones addressed in comments). The first is that you use the decimal code for [] instead of the hexa, so you should use \x5b\x5d instead of \x91\x93. The second problem is that if you do use the proper codes, sed will still interpret those syntactically as []. So you can't escape escaping. Here's what you should call:
sed -ri'.bkup' '184s!\[\]![\x27--timeout=35\x27]!' /var/www/Varnish/lib/Extra/SG/ObjectPoller2.pm
And this will create the backup for you (but you should double check).

setting variables in bash script

I have the following command:
echo "exec [loc_ver].[dbo].[sp_RptEmpCheckInOutMissingTime_2]" |
/home/mdland_tool/common/bin/run_isql_prod_rpt_2.sh |
grep "^| Row_2" |
awk '{print $15 }'
which only works with echo in the front. I tried to set this line into a variable. I've tried quotations marks, parenthesis, and back ticks, with no luck.
May anyone tell me the correct syntax for setting this into a variable?
If you want more columns store in an array you should use this syntax (it is also good if you have only one result):
#!/bin/bash
result=( $( echo "exec [loc_ver].[dbo].[sp_RptEmpCheckInOutMissingTime_2]" |
/home/mdland_tool/common/bin/run_isql_prod_rpt_2.sh |
grep "^| Row_2" |
awk '{print $15 }' ) )
$result=$(exec [loc_ver].[dbo].[sp_RptEmpCheckInOutMissingTime_2]" | /home/mdland_tool/common/bin/run_isql_prod_rpt_2.sh | grep "^| Row_2" | awk '{print $15 })
Since you asked for both meanings of your question:
First, imagine a simpler case:
echo asdf
If you want to execute this command and store the result somewhere, you do the following:
$(echo asdf)
For example:
variable=$(echo asdf)
# or
if [ "$(echo asdf)" == "asdf" ]; then echo good; fi
So generally, $(command) executes command and returns the output.
If you want to store the text of the command itself, you can, well, just do that:
variable='echo asdf'
# or
variable="echo asdf"
# or
variable=echo\ asdf
Different formats depending on the content of your command. So now once you have the variable storing the command, you can simply execute the command with the $(command) syntax. However, pay attention that the command itself is in a variable. Therefore, you would execute the command by:
$($variable)
As a complete example, let's combine both:
command="echo asdf"
result=$($command)
echo $result

Resources