User Defined Function in Awk - shell

I am facing as issue in below mentioned call to User Defined function using AWK
awk '{
if ("${PROS}" == "" )
wq_fm_exception ${FILE_SEQ_NBR}
#exit 1
HEAD="TRUE"
}
where wq_fm_exception is a user defined function being written in same script in which awk is being called. The parameter name ${FILE_SEQ_NBR} passed to function wq_fm_exception is defined globally.
Can anyone suggest?

AWK cannot call functions defined in the spawning shell. You could, however, have it write its error to a file and exit with failure, and have the spawning shell then deal with that:
if ! awk '{
if(...) {
print "can't frobnicate the bar" > "/tmp/errors"
exit 1
}
}'; then
wq_fm_exception "$(cat /tmp/errors)"
exit $?
fi
(If you choose to go with this method, you’d probably want to use mktemp to generate a filename for /tmp/errors rather than hardcoding it.)

Related

Bash script does nothing when I run it, seems to keep waiting

I've written my first script, one in which I want to know if 2 files have the same values in a specific column.
Both files are WEKA machine-learning prediction outputs for different algorithms, hence they have to be in the same format, but the prediction column would be different.
Here's the code I've written based on the tutorial presented in https://linuxconfig.org/bash-scripting-tutorial-for-beginners:
#!/bin/bash
lineasdel1=$(wc -l $1 | awk '{print $1}')
lineasdel2=$(wc -l $2 | awk '{print $1}')
if [ "$lineasdel1" != "$lineasdel2" ]; then
echo "Files $1 and $2 have different number of lines, unable to perform"
exit 1
fi
function quitalineasraras {
awk '$1!="==="&&NF>0'
}
function acomodo {
awk '{gsub(/^ +| +$/, ""); gsub(/ +0/, " W 0"); gsub(/ +1$/, " W 1"); gsub(/ +/, "\t") gsub(/\+\tW/, "+"); print}'
}
function procesodel1 {
quitalineasraras "$1" | acomodo
}
function procesodel2 {
quitalineasraras "$2" | acomodo
}
el1procesado=$(procesodel1)
el2procesado=$(procesodel2)
function pegar {
paste <(echo "$el1procesado") <(echo "$el2procesado")
}
function contarintersec {
awk 'BEGIN {FS="\t"} $3==$8 {n++} END {print n}'
}
unido=$(pegar)
interseccion=$(contarintersec $unido)
echo "Estos 2 archivos tienen $interseccion coincidencias."
I ran all individual codes of all functions in the terminal and verified they work successfully (I'm using Linux Mint 19.2). Script's permissions also have been changed to make it executable. Paste command also is supposed to work with that variable syntax.
But when I run it via:
./script.sh file1 file2
if both files have the same number of lines, and I press enter, no output is obtained; instead, the terminal opens an empty line with cursor waiting for something. In order to write another command, I've got to press CTRL+C.
If both files have different number of lines the error message prints successfully, so I think the problem has something to do with the functions, with the fact that awk has different syntax for some chores, or with turning the output of functions into variables.
I know that I'm missing something, but can't come up with what could be.
Any help will be appreciated.
what could be.
function quitalineasraras {
awk '$1!="==="&&NF>0'
}
function procesodel1 {
quitalineasraras "$1" | acomodo
}
el1procesado=$(procesodel1)
The positional variables $1 are set for each function separately. The "$1" inside procesodel1 expands to empty. The quitalineasraras is passed one empty argument "".
The awk inside quitalineasraras is passed only the script without the filename, so it reads the input for standard input, ie. it waits for the input on standard input.
The awk inside quitalineasraras without any file arguments makes your script seem to wait.

Bash function returns an unexpected value

I wrote this function in BASH, and it returns an unexpected value:
checkIfUserFound()
{
isUserFound=$( cat user_uid.txt | grep $ADMIN_TO_UPDATE -B 1 | grep "uid" )
return $isUserFound
}
the user_uid.txt file is empty (I enter invalid admin).
but for some reason, it returns "1":
checkIfUserFound
isUserFound=$?
if [ "$isUserFound" -eq "0" ];
then
echo "Searching..."
else
echo "User found..."
fi
This prints "User found..."
Does anyone know how come that is the returning value ? Shouldn't I get "0" when returning from the function ?
The argument to return needs to be a number (a small integer -- the range is 0 through 255). But grep already sets its return code to indicate whether it found a match, and functions already return the return code of the last command in the function; so all you really need is
checkIfUserFound()
{
grep "$ADMIN_TO_UPDATE" -B 1 user_uid.txt |
grep -q "uid"
}
(Notice also how we get rid of the useless use of cat).
The script could probably usefully be refactored to Awk. Perhaps I am guessing correctly what you want:
checkIfUserFound()
{
awk -v user="$ADMIN_TO_UPDATE" '/uid/ { p=1; next }
p { if ($0 ~ user) found=1; p=0 }
END { exit 1-found }' user_uid.txt
}
Finally, the code which calls this function should check whether it succeeded, not whether it printed 0.
if ! checkIfUserFound
then
echo "Searching..."
else
echo "User found..."
fi
Notice perhaps that [ is not part of the if command's syntax (though [ is one of the commands whose result code if is often used to check).
The main problem is that you don't check for failure of any of the commands in the pipe.
The failure (exit code) of the last command in the pipe will be the exit value of the whole pipe. That exit code is available in the $? variable.
And since you want to return the success/failure of the pipe from the function, that's the value you need to return:
return $?
If the pipe doesn't fail then the result of the pipe will be the output on stdout, which will be put into the $isUserFound variable. Since it will not be a valid exit code (which must be numeric) then it can't be returned.
Since you don't need the $isUserFound variable, and you want to return the result of the pipe, your function could be simplified as
checkIfUserFound() {
cat user_uid.txt | grep $ADMIN_TO_UPDATE -B 1 | grep "uid" 2>&1 >/dev/null
}

Modify a shell variable inside awk block of code

Is there any way to modify a shell variable inside awk block of code?
--------- [shell_awk.sh]---------------------
#!/bin/bash
shell_variable_1=<value A>
shell_variable_2=<value B>
shell_variable_3=<value C>
awk 'function A(X)
{ return X+1 }
{ a=A('$shell_variable_1')
b=A('$shell_variable_2')
c=A('$shell_variable_3')
shell_variable_1=a
shell_variable_2=b
shell_variable_3=c
}' FILE.TXT
--------- [shell_awk.sh]---------------------
This is a very simple example, the real script load a file and make some changes using functions, I need to keep each value before change into a specific variable, so then I can register into MySQL the before and after value.
The after value is received from parameters ($1, $2 and so on).
The value before I already know how to get it from the file.
All is done well, except the shell_variable been set by awk variable. Outside from awk block code is easy to set, but inside, is it possible?
No program -- in awk, shell, or any other language -- can directly modify a parent process's memory. That includes variables. However, of course, your awk can write contents to stdout, and the parent shell can read that content and modify its own variables itself.
Here's an example of awk that writes key/value pairs out to be read by bash. It's not perfect -- read the caveats below.
#!/bin/bash
shell_variable_1=10
shell_variable_2=20
shell_variable_3=30
run_awk() {
awk -v shell_variable_1="$shell_variable_1" \
-v shell_variable_2="$shell_variable_2" \
-v shell_variable_3="$shell_variable_3" '
function A(X) { return X+1 }
{ a=A(shell_variable_1)
b=A(shell_variable_2)
c=A(shell_variable_3) }
END {
print "shell_variable_1=" a
print "shell_variable_2=" b
print "shell_variable_3=" c
}' <<<""
}
while IFS="=" read -r key value; do
printf -v "$key" '%s' "$value"
done < <(run_awk)
for var in shell_variable_{1,2,3}; do
printf 'New value for %s is %s\n' "$var" "${!var}"
done
Advantages
Doesn't use eval. Content such as $(rm -rf ~) in the output from awk won't be executed by your shell.
Disadvantages
Can't handle variable contents with newlines. (You could fix this by NUL-delimiting output from your awk script, and adding -d '' to the read command).
A hostile awk script could modify PATH, LD_LIBRARY_PATH, or other security-sensitive variables. (You could fix this by reading variables into an associative array, rather than the global namespace, or by enforcing a prefix on their names).
The code above uses several ksh extensions also available in bash; however, it will not run with POSIX sh. Thus, be sure not to run this via sh scriptname (which only guarantees POSIX functionality).

Find FileVault2 user in fdesetup output with Awk

I have 2 user profiles (fred, fredmoo) with filevault 2 enabled.
I have the following bash:
## Get the logged in user's name
userName=$(/usr/bin/stat -f%Su /dev/console)
This first user check sees if the logged in account is already authorized with FileVault 2
userCheck=`fdesetup list | awk -v usrN="$userName" -F, 'index($0, usrN) {print $1}'`
if [ "${userCheck}" != "${userName}" ]; then
echo "This user is not a FileVault 2 enabled user."
exit 3
fi
If I do echo $userName, I get
fred
If I do echo $userCheck, I get
fred fredmoo
The conditional statement above works well if there's only 1 profile. Unix or Linux Image, however since my mac has more than 1 user profile, the statement will echo "This user is not a FileVault 2 enabled user." and exit.
userCheck has both profiles.
How do I modify the if statement to say if userName does NOT equal to the 1st userCheck or 2nd userCheck, then echo "This user is not a FileVault 2 enabled user." and exit?
The output from fdesetup list looks like this:
fredmoo,485A09Cf-B0D5-469A-8224-2DD1877E780B
administrator,DDB87E8D-8150-4D06-A59D-774AD28D119C
gollum,8AE6C365-E38F-49E2-998C-F4742CC9980C
Your Awk script looks for fred anywhere in the fdesetup output, which of course it also finds when the output contains fredmoo.
You seem to have a comma-separated output that you are comparing against, so your Awk script should probably be something like awk -v user="$(whoami)" -F , '$1 == user { print $1 }' (where I assume output looks like in the sample here: https://derflounder.wordpress.com/2013/10/22/managing-mavericks-filevault-2-with-fdesetup/).
Also, a common antipattern is storing stuff you only need once in a variable, which just complicates things and pollutes your namespace. Try to change your Awk script so that it sets the correct exit code; then you can use that directly in a conditional. Maybe even refactor that into a reusable function.
warn () {
echo "$0: $#" >&2
}
die () {
rc=$1
shift
warn "$#"
exit $rc
}
canihazfv2 () {
fdesetup list |
awk -v user="$1" -F , '$1 == user { print $1; exit 0 } END { exit 1 }'
}
me=$(whoami)
canihazfv2 "$me" || die 3 "$me is not enabled to use FileVault 2"
Notice also how the script identifies itself in the error message (so that when you build new scripts which call other scripts which call this script, you can see which one is actually failing three years from now) as well as the actual input which triggered the error, and prints the error message to standard error through redirection.
As always, this || that is shorthand for if ! this; then that; fi where of course the longhand might be preferable if that is something moderately complex (which I avoid here by also encapsulating die in a function).

Passing and return value of external function in awk

I am calling function from another script from awk.
Why i need to press enter even after adding /dev/null
Why the passed argument is not displayed. I am getting spaces.
I am not getting the return value from external function.
cat mainpgm.sh
#!/bin/bash
key="09"
awk -v dk="$key" ' { ma = system(". /home/ott/functions.sh;derived dk")</dev/null ; "print returned value" ma } '
cat functions.sh
#!/bin/bash
derived () {
echo "outside function" $dk
return 9
}
If you don't want to process input in awk, redirect its input to /dev/null, and do everything in the BEGIN block. Also, for the dk variable to be replaced with its value, it has to be outside the quotes.
awk -v dk="$key" 'BEGIN {
ma = system(". /home/ott/functions.sh;derived " dk);
print "returned value", ma
}' < /dev/null
To answer your questions:
You put /dev/null in the wrong place. It's supposed to be the input of the script, not the system function.
Two reasons: First, you put dk inside the quotes, so its value is not substituted. Second, the derived function doesn't print its argument, it prints $dk, which is a nonexistent shell variable. The argument to a function is $1, so it should do:
echo "outside function $1"
You had print inside the quotes, so it wasn't being executed as a statement.

Resources