1

I wrote the following script in the test.sh:

#!/bin/sh
compare() { 
if [ $1 != root ]; then 
echo "Fail" >> CAT1.txt 
fi 
}
awk -F: '$4 == 0' /etc/passwd | cut -d: f1 | xargs -n1 -i bash -c 'compare "$@"' _

when I execute this scripts, get the following error:

_: compare: command not found
  • functions are not exported by default to child processes. You need to add export -f compare after the function. Also prefer #!/bin/bash when using bash-isms, as /bin/sh may not be the same as bash. – meuh Jul 7 '15 at 8:27
  • I export it, but i get the error: export: illegal option -f – Gohar Jul 7 '15 at 8:53
  • 1
    Use #!/bin/bash as the start line. – meuh Jul 7 '15 at 9:13
2

A function is internal to the shell that defines it. If you run another program, that other program won't see the function, even if it also happens to be a shell.

(Functions are accessible in subshells, i.e. when a copy of the running shell is made to run something in parentheses (…) or for a command substitution $(…) etc. But they are not accessible in separate programs, e.g. when you run sh -c ….)

Define the function in the script that uses it:

#!/bin/sh
awk -F: '$4 == 0' /etc/passwd | cut -d: f1 | xargs -n1 -i bash -c '
  compare() { 
  if [ $1 != root ]; then 
  echo "Fail" >> CAT1.txt 
  fi 
  }
  compare "$@"' _

(I assume this is a toy example, this could all be done in a single, simple awk script.)

Alternatively, you can use a bash feature which allows functions to be exported through the environment to a child instance of bash. This is a bash-specific feature, so the parent script would have to run bash, not sh.

#!/bin/bash
compare() { 
if [ $1 != root ]; then 
echo "Fail" >> CAT1.txt 
fi 
}
export -f compare
awk -F: '$4 == 0' /etc/passwd | cut -d: f1 | xargs -n1 -i bash -c 'compare "$@"' _
0
  1. why not use ... ?

    awk -F: '$4==0 && $1 != root ' /etc/passwd > CAT1.txt
    

    1.1 edit to get Fail

    awk -F: '$4==0 && $1 != root {print "Fail" ; } /etc/passwd > CAT1.txt
    

    to get fail AND user name

    awk -F: '$4==0 && $1 != root {printf "Fail %s\n",$1 ; } /etc/passwd > CAT1.txt
    
  2. function definition is lost when forking the new bash. Ae @mueh told you, you need to export -f compare.

  • '$4==0 && $1 != root{print "Fail"}' – Costas Jul 7 '15 at 8:35
  • @costas true, this will write fail for any user gid0, not root, but without username ! – Archemar Jul 7 '15 at 8:47
  • I want to learn how to use function in a script, too! – Gohar Jul 7 '15 at 8:56
0

You should do this w/ a forking logic of some kind. Set some marker in your script's environment which will definitely prove to child instances of your script that they should do one thing while the parent does another. In that way you can make the script safely recursive.

#!/bin/sh
case $Z:$P in 
("$0:${1:-!$P}")
     [ root = "$2" ] ||
     ! echo Fail
;;(*)
     set -a; Z=$0 P=$$
     </etc/passwd     \
     awk -F: '$4 == 0'|
     cut -d: -f1      |
     xargs -n1 "$0" "$$"
esac >>CAT1.txt

Or, maybe use your fds to do the check. If you're running the script in the first place from a terminal then...

#!/bin/sh
[ -t 0 ] && {
     awk '$4 == 0' |
     cut   -d: -f1 |
     xargs -n1 "$0"
     exit
} </etc/passwd >>CAT1.txt
[ root = "$1" ]   ||
! echo Fail

But the best way is actually to change the child's $0:

#!/bin/sh
[ . = "$0" ] && {
    [ root = "$1" ] ||
    ! echo Fail
}
z=$(head -n5 <"$0")
</etc/passwd  \
awk '$4 == 0' |
cut -d:  -f1  |
>>CAT1.txt    \
xargs -n1     \ 
sh -c "$z" . 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.