shell scripting: nested subshell ++ - bash

More than a problem, this is a request for "another way to do this".
Actually, if I want to use the result of a previous command in another one, I use:
R1=$("cat somefile | awk '{ print $1 }'" )
myScript -c $R1 -h123
then, a "better way" is:
myScript -c $("cat somefile | awk '{ print $1 }'" ) -h123
But what if I have to use the result several times? Let's say: using several times $R1, well the 2 options are:
Option 1
R1=$("cat somefile | awk '{ print $1}'")
myScript -c $R1 -h123 -x$R1
option 2
myScript -c $("cat somefile | awk '{ print $1 }'" ) -h123 -x $("cat somefile | awk '{ print $1 }'" )
Do you know another way to "store" the result of a previous command/script and use it as a argument into another command/script?

Sure, there are other ways. They're just not better ways.
First, you could store the answer in a file, and then cat the contents of the file multiple times.
Second, you could pass the results to a bash function like:
callMyScript() {
myScript -c "$1" -h123 -x "$1"
}
invoked thusly:
callMyScript "$(awk '{ print $1; }' somefile)"
which is almost precisely identical to just saving off into a local variable.
So you're interested in using awk? You could have awk generate the line for you, and have bash run it:
eval $(awk '{ printf "myScript -c %s -h123 -x %s\n", $1, $1; }' somefile)
but now we're just getting silly, and even that's no different conceptually from simply saving off into a variable.
My advice: Use the variable.

Another not perfect way:
source script #1, then when script #2 runs all of the variables declared in #1 are available to #2.
#!/bin/ksh
. myScript -c $("cat somefile | awk '{ print $1 }'" ) -h123
myScript2
Your best choice is a wrapper script that keeps all your variables for you, as everyone else already noted.
In bash the source builtin command is the same as the 'dot'

Related

echo "$var" prints blank space

I was basically trying to compare two files and as part of that I assigned the cksum of the file to a variable . But when I try to compare it, it did not work. I realized that when I tried to read the variable nothing gets printed out
The below commands worked just fine
s.joseph#VA-S-JOSEPH-900 /cygdrive/c/users/Anuprita
$ test=`cksum interface2 | awk -F" " '{ print $1 }'`
s.joseph#VA-S-JOSEPH-900 /cygdrive/c/users/Anuprita
$ echo "$test"
3021988741
But when these are part of a script and I try to echo $var, nothing gets printed
$ for i in `ls interface*`;
do chksum1=`cksum $i | awk -F" " '{ print "'$1'" }'`;
echo "$chksum1";
done
s.joseph#VA-S-JOSEPH-900 /cygdrive/c/users/Anuprita
$
I am using bash shell
Without assigning it to any variable, the output is as shown below
for i in interface*; do echo "interface=\"$i\""; cksum "$i"; done
interface="interface11"
4113442291 111 interface11
interface="interface17"
1275738681 111 interface17
interface="interface2"
3021988741 186 interface2
Looks like it is an issue only with bash on cygwin. The script seems to be working just fine on unix
for i in ls interface*; do chksum1=cksum $i | awk -F" " '{ print $1 }'; echo $i, $chksum1; done
interface1, 4294967295
interface2, 4294967295
Try this;
for i in ls interface*; do echo "interface=$i"; chksum1=$(cksum $i | awk -F" " '{ print "'$1'" }'); echo "$chksum1"; done
I like adding the echo statement to verify your getting what you think with the ls statement and the variable assignment should use $(cmd) or `cmd`
Cheers
What you have in your 2nd script:
print "'$1'"
is a completely different statement from what you have in your first one:
print $1
Think about it and ask yourself why you changed it and what it is you're trying to achieve. Also man awk and see g at http://cfajohnson.com/shell/cus-faq-2.html#Q24 for what print "'$1'" does.
Best I can tell without and provided sample input your script should be written:
for i in interface*; do chksum1=$(cksum "$i" | awk '{ print $1 }'); echo "$chksum1"; done

Adding value to global variable in a subshell is not working

I am trying to get the total disk usage of my machine. Below is the script code:
#!/bin/sh
totalUsage=0
diskUse(){
df -H | grep -vE '^Filesystem|cdrom' | awk '{ print $5 " " $1 }' | while read output;
do
diskUsage=$(echo $output | awk '{ print $1}' | cut -d'%' -f1 )
totalUsage=$((totalUsage+diskUsage))
done
}
diskUse
echo $totalUsage
While totalUsage is a global variable, I have tried to sum the individual disk usage to totalUsage in the line:
totalUsage=$((totalUsage+diskUsage))
An echo of totalUsage between do and done shows the correct value,
but when I try to echo it after my call diskUse, it stills prints a 0
Can you please help me, what is wrong here?
The variable totalUsage in a sub-shell doesn't change the value in the parent shell.
Since you tagged bash, you can use here string to modify your loop:
#!/bin/bash
totalUsage=0
diskUse(){
while read output;
do
diskUsage=$(echo $output | awk '{ print $1}' | cut -d'%' -f1 )
totalUsage=$((totalUsage+diskUsage))
done <<<"$(df -H | grep -vE '^Filesystem|cdrom' | awk '{ print $5 " " $1 }')"
}
diskUse
echo $totalUsage
I suggest to insert
shopt -s lastpipe
as new line after
#!/bin/bash
From man bash:
lastpipe: If set, and job control is not active, the shell runs the last command of a pipeline not executed in the background in the current shell environment.

how to change a string to a path in ksh

ls -lAtr /data/log.* | tail -1 | awk '{ printf $9 }' > $logfile
echo $logfile
cat $logfile # I want to cat the content of this log file, but this wouldn't work
logfile2=/usr/some/path/text.log
echo $logfile2
cat $logfile2 # This work
I am new to shell programming, I wondering how do I convert the logfile into something like logfile2(Did I ask the right question?), so that I can treat it like a file and read from it.
Think you're looking for (works in bash as well)
logfile2="$(</usr/some/path/text.log)"
From ksh man page
$(cat file) can be replaced by the equivalent but faster $(<file).
e.g.
> cat text.log
line 1
line 2
> ksh
> logfile2="$(<text.log)"
> echo "$logfile2"
line 1
line 2
Are you trying to store the result of ls|tail|awk in $logFile? If so:
logFile=$(ls -lAtr /data/log.* | tail -1 | awk '{ printf $9 }')
However, you shouldn't parse the output of ls.

Bash variable from command with pipes, quotes, etc

I have a command I'm using to get the hostname.localdomain:
dig axfr #dc1.localdomain.com localdomain.com | grep -i Lawler | awk '{ getline ; $1=substr($1,1,length($1)-1); print $1 ; exit }'
This nicely returns a result like:
michael.lawler.localdomain.com
I'd like to further use that result as a variable in a Bash script.
It seems I'm having trouble getting past the first pipe.
If I VAR="dig axfr #dc1.localdomain.com localdomain.com | grep -i Lawler | awk '{ getline ; $1=substr($1,1,length($1)-1); print $1 ; exit }'"
...I get back the entire zone transfer. I've also tried many minor changes, adding $ before the dig command, without quotes, but nothing seems to work. How can I fix this?
VAR=$( dig axfr #dc1.localdomain.com localdomain.com |
grep -i Lawler |
awk '{ getline ; $1=substr($1,1,length($1)-1); print $1 ; exit }' )
Use backtics instead of quotes:
VAR=`dig axfr #dc1.localdomain.com localdomain.com | grep -i Lawler | awk '{ getline ; $1=substr($1,1,length($1)-1); print $1 ; exit }'`
Backtics actually mean "run whatever is in here and return standard out as the expression's value", but quotes don't do that.

Calling Awk in a shell script

I have this command which executes correctly if run directly on the terminal.
awk '/word/ {print NR}' file.txt | head -n 1
The purpose is to find the line number of the line on which the word 'word' first appears in file.txt.
But when I put it in a script file, it doens't seem to work.
#! /bin/sh
if [ $# -ne 2 ]
then
echo "Usage: $0 <word> <filename>"
exit 1
fi
awk '/$1/ {print NR}' $2 | head -n 1
So what did I do wrong?
Thanks,
Replace the single quotes with double quotes so that the $1 is evaluated by the shell:
awk "/$1/ {print NR}" $2 | head -n 1
In the shell, single-quotes prevent parameter-substitution; so if your script is invoked like this:
script.sh word
then you want to run this AWK program:
/word/ {print NR}
but you're actually running this one:
/$1/ {print NR}
and needless to say, AWK has no idea what $1 is supposed to be.
To fix this, change your single-quotes to double-quotes:
awk "/$1/ {print NR}" $2 | head -n 1
so that the shell will substitute word for $1.
You should use AWK's variable passing feature:
awk -v patt="$1" '$0 ~ patt {print NR; exit}' "$2"
The exit makes the head -1 unnecessary.
you could also pass the value as a variable to awk:
awk -v varA=$1 '{if(match($0,varA)>0){print NR;}}' $2 | head -n 1
Seems more cumbersome than the above, but illustrates passing vars.

Resources