Sign up ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

I'm looking for something similar to Bash's built-in command that will only run something if it is a function. So currently I have an insecure way of doing:

# Go through arguments in order
for i in $*; do
    if [ -z `which $i` ]; then
        # Run function
        $i && echo -n ' '
    fi
done

This if statement doesn't work properly. Anyway, even if I could check if it's a command and not a function, I can't run explicitly run a function, which is bad because if anyone has any programs in $PATH that are the same name as my function, they will be run. If I nullify PATH or set it to anything else, anyone could still use $i to run a program explicit, so that's also not a solution.

Any way I can "secure" my shell script?

share|improve this question
2  
This looks like a really poor way to work around just having proper argument parsing.. –  kyrias Jul 21 at 3:28
    
Right, so instead of functions, can I use something like e.g xargs to run certain bash functions? I dunno, I feel pretty limited with bash compared to something like Python, Perl or even C where I know if I run the name of a function from an argument, it's not going to run anything else. –  Mrrhq Jul 21 at 3:37

2 Answers 2

up vote 3 down vote accepted

With bash, you can do something like this:

for f do
  if declare -F -- "$f" >/dev/null 2>&1; then
    : "$f" is a function, do something with it
  fi
done

declare -F -- "$f" >/dev/null 2>&1 will return success code if $f is a bash function, output nothing.

You might also want to disable some special builtin commands when bash run in POSIX mode by adding builtin enable -n -- "$f".

share|improve this answer
    
This is really supposed to work? for f do? Shouldn't that be for f in <something> do instead? I'm wonder if a certain boolean condition could actually turn this into an infinite loop... –  syntaxerror Jul 21 at 4:26
4  
@syntaxerror: for f do is shorthand and POSIX way of for f in "$@"; do. –  cuonglm Jul 21 at 4:27
    
Oh thanks, didn't know that. I'm not going to use it though; the latter variant you quoted will increase readability by at least 25 percent (IMHO), since the $@ will even be understood by bash programmers who only know the more basic syntax. –  syntaxerror Jul 21 at 4:29
1  
one note. you should explicitly quote $f in eval to prevent an alias expansion in Posix-mode (or with shopt -s expand_aliases). See example –  Evgeny Vereshchagin Jul 21 at 5:12
1  
There is another edge case in Posix-mode: a function with the same name as the special builtin. See example –  Evgeny Vereshchagin Jul 21 at 8:43

While you're probably well set in a bash shell, to do similar portably you might employ something like the following...

eval "  for c in $(unalias -a
        for c do case ${c#function}  in
                (*[!_[:alnum:]]*) ;; (?*)
                PATH=   command -v "$c" >&2 &&
                PATH=   command -V "$c"     &&
                        printf '\n'"'$c'"'\\\0'
        esac 2>/dev/null; done  |
        tr  '\t\n\0' '  \n'     |
        sed -ne's/.* function .* / /p')"'
;do     "$c"; printf %02s
done'

It works in stages:

  1. First weed out all args which contain any characters that might disqualify them as shell names in the first place.
  2. If the current arg is a valid name and a valid shell builtin or function print its name and type to stdout. Because of the way shell's can vary their output for command -V its output cannot be counted upon to be a single line. So follow the output with our verified name and and a null-byte.
  3. translate all newlines and tabs to spaces, and all null-bytes to newlines.
  4. with sed, s///ubstitute away all of any line up the last space character thereon and print the results if the line also matches the string function.
  5. All of sed's output is rounded up as iterators for the outer for loop and each of these is executed in turn, and after each is executed printf writes out two spaces.
share|improve this answer
    
Try this code. There are three functions in the script, but dash script foo bar cd detects only bar function. –  Evgeny Vereshchagin Jul 21 at 18:57
    
@EvgenyVereshchagin - yeah, that was the point - to avoid aliases and builtins. If the opposite behavior is desired, then for aliases i can just unconditionally unalias (and add a quote to echo "$c"), and for builtins i can just test for altered output. The whole thing can be done simply like case $(command -V -- "$c") in (*"$c"*function*|*function*"$c"*)... but it requires a subshell every test. –  mikeserv Jul 21 at 19:24
    
thanks for the explanation. declare -F-bashism detects everything. +1 for magical loop body:) –  Evgeny Vereshchagin Jul 21 at 19:46
    
@EvgenyVereshchagin - you know what, I thought about it, and you had a pretty good point. Try it again. –  mikeserv Jul 21 at 22:39
1  
@EvgenyVereshchagin - Oh. Well then it just does the right thing and refuses to define a function named set(). That has nothing to do with my code. Line 10 is the set(){ ... } line. And yes, bash is the worst - it is the slowest, buggiest shell I've ever used. Here are some tests. –  mikeserv Jul 22 at 5:53

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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