Passing and return value of external function in awk - bash

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.

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.

Nested dollar signs inside quotes

Trying to write a bash script containing nested dollar variables and I can't get it to work :
#!/bin/bash
sed '4s/.*/$(grep "remote.*$1" /home/txtfile)/' /home/target
The error says :
sed / -e expression #1, char 30: unkown option to 's'
The problem seems to come from $1 which need to be replaced by the parameter passed from the bash call and then the whole $(...) needs to be replaced by the command call so we replace the target line 4 by the string output.
Variable expansion and Command substitution won't be done when put inside single quotes, use double quotes instead:
sed "4s/.*/$(grep "remote.*$1" /home/txtfile)/" /home/target
Your approach is wrong, the right way to do what you want is just one command, something like this (depending on your possible $1 values and input file contents which you haven't shown us):
awk -v tgt='remote.*$1' '
NR==FNR { if ($0 ~ tgt) str = str $0 ORS; next }
FNR==4 { printf "%s", str; next }
{ print }
' /home/txtfile /home/target

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).

passing command line argument to gawk script

I have a script chk.awk to which I want to pass some command line arguments. It has awk statements, sed command etc. Just for example I have taken a small program below to which I want to pass command line arguments.
#!/bin/bash
var1=$1
gawk '
BEGIN {
printf "argc = %d\n argv0=%s\n argv1=%s\n var1=%s\n",ARGC,ARGV[0],ARGV[1],$var1
}'
But when I try :
$ sh chk.awk 10 20
argc = 1
argv0=gawk
argv1=
var1=
Above I tried to display the command line arguments by both ways i.e. argv & $1, but none of them work. Can anyone let me know where I am going wrong here? What is the correct way to do that?
The problem is that you give arguments to the shell script, but not to the awk script.
You must add "$#" to the call of gawk.
#!/bin/bash
var1=$1
gawk '
BEGIN {
printf "argc = %d\n argv0=%s\n argv1=%s\n var1=%s\n",ARGC,ARGV[0],ARGV[1],$var1
}' "$#"
Otherwise you will your arguments in the shell-script and they will be not passed to gawk.
Update 1
If you have additional args (e.g. filenames that are to be processed),
you must remove the first portition of args first (in the BEGIN section):
#!/bin/bash
var1=$1
gawk '
BEGIN {
printf "argc = %d\n argv0=%s\n argv1=%s\n var1=%s\n",ARGC,ARGV[0],ARGV[1],$var1;
delete ARGV[1]
}' "$#" filename

ksh + print the last value from parameter without print the $parameter

I need advice - how to print the same last value in ksh scripts without to print param argument
so what we can do in ksh inorder to print the last value ?
example - I need to print the last value ( in this case -$ETH_PORT ) , without to define $ETH_PORT parameter after the second echo command
how to print the last value from the last echo/print command?
function test
{
ETH_PORT=eth0
echo $ETH_PORT
# now I need to print the last value ( in this case value from $ETH_PORT param )
echo < what need to write in order to print last value >
}
test
expected output after runing the test function
eth0
eth0
You could subvert echo with a function that kept track of the last thing echo'ed, but that wouldn't capture the last thing to go to stdout:
#!/bin/bash
function echo {
/bin/echo $*
last_echoed="$*"
}
function testit {
ETH_PORT=eth0
echo "this is not echo'ed twice"
echo $ETH_PORT
echo $last_echoed
}
testit
A second option is to use a wrapper script that keeps track of what the last line was and then print it at the end.

Resources