Pregunta

Is there a way to set the debug mode(set -x) on a KornShell (ksh) script globally? Currently it seems I have do something like the following:

a(){
   set -x
   #commands
}

b(){
   set -x
   #more commands
}

set-x 
a
#commands
b

I would really like to only have to call the set-x command in one place.

Note: This is all in KSH88 on AIX.

Example:

#!/bin/ksh
set -x

a(){
   echo "This is A!"
}

b(){
   echo "This is B!"
}

a
echo "Outside"
b
dev2:/home/me-> ./testSetX
+ a
This is A!
+ echo Outside
Outside
+ b
This is B!
dev2:/home/me->
¿Fue útil?

Solución

This is ksh88 on an HP-UX machine:

me@host ..dev/
$ cat ./test/verbose
#!/bin/ksh
set -x

hello() {
  print $1
}

hello kapow!
exit

me@host..dev/
$ ./test/verbose    
+ hello kapow!
+ print kapow!
kapow!
+ exit

It sure looks like that works fine. I validated that it also works with a "set -x" anywhere before the first function call.

I moved to an AIX system, and experienced the problem you described. When functions are defined as either function a { or a() { in AIX ksh88, the set -x doesn't appear to carry forward into the function-local scope. Switching to ksh93 on the same AIX box, functions declared using the new function a { syntax also don't carry the outer set -x into the inner scope. However, ksh93 behaves like POSIX sh (and ksh88 on other platforms) used to behave, carrying the set -x through to the function when the function is defined in the old a(){ method. This is probably due to the backwards compatability in ksh93, where it tries to emulate the old behavior when functions are defined the old way.

Therefore, you might be able to temporarily switch the interpreter over to ksh93 for debugging purposes, and then switch back to ksh88 if you don't like having the longer arrays, associative arrays, floating point math, namespace support, and rougly 10x improvement in execution speed which ksh93 brings. ;) Because it looks like the answer is "no, you can't do that" with ksh88 on AIX. :(

Otros consejos

Add it to your shebang line:

#!/bin/ksh -x

Or set it at the top of your script:

#!/bin/ksh
set -x

Or start your script from the command line:

ksh -x script_name

I tested a global set -x with ksh88 (on Solaris 10) and ksh93 (Fedora 17) and with both a global set -x command at the top of the script does not have function-local scope (i.e. it does not have any local effects).

As a workaround you can enable local command tracing for all functions in scope (after they are defined) and before they are called via typeset:

$ cat test.ksh
PS4='$LINENO: '

set -x

function foo {
  print Hello
}

bar() {
  print World
}

typeset -ft `typeset +f` 

foo
bar

Output under ksh88 (Solaris 10):

$ ksh test.ksh 
13: typeset +f
13: typeset -ft bar foo
15: foo
1: print Hello
Hello
16: bar
1: print World
World

Typeset commented out

$ ksh test.ksh 
15: foo
Hello
16: bar
World

Output under ksh93 (Fedora 17):

$ ksh test.ksh
13: typeset +f
13: typeset -ft 'bar()' foo
15: foo
6: print Hello
Hello
16: bar
10: print World
World

Typeset commented out

$ ksh test.ksh
15: foo
Hello
16: bar
10: print World
World

Output under bash

typeset commented out and print substituted with echo:

$ bash test.ksh
15: foo
6: echo Hello
Hello
16: bar
10: echo World
World

(bash 4.2.39(1) on Fedora 17)

Same output under zsh 5.0.2 on Fedora 17.

Conclusion

When using Ksh, only with ksh93 and the fnname() function definition syntax a global set -x also has local scope. The typeset -ft based workaround is a relatively light way to enable command tracing for all functions.

In bash (and zsh) a global set -x works as expected, i.e. it also has local scope for all functions.

Thus, when writing new scripts using bash instead of ksh might be the better alternative because of this.

As a side note: bash is probably even more portable than ksh88 - especially more portable than ksh93.

Be aware that scoping is impacted between the two types of function declarations

a() { }

vs.

function a { }

In particular, the typset under ksh doesn't work as you would expect in the former case. Found out while trying to debug a script with a recursive function. More here:

http://www.dartmouth.edu/~rc/classes/ksh/functions.html

The behavior of "set -x" is a "peculiarity" of the AIX shell (not to say brain d...).

You asked for "a way to set the debug mode... globally". Here is what I do to achieve this:

set_x="${set_x-:}"; # Defaults to ":" (NOOP) unless already non-empty
# Using ":" instead of "" makes termination via semicolon work,
# which in turn allows inlining, especially when using SSH.
# Alternatively, if tracing should be on by default:
#set_x="${set_x-set -x}"; # Defaults to "set -x" unless already non-empty

$set_x; # Apply to file scope

f() {
    $set_x; # Apply to local scope
    echo working...;
}
main() {
    $set_x; # Apply to local scope
    f;
    ssh localhost $set_x\; hostname; # Apply to remote shell scope
    ssh localhost "set_x=\"$set_x\" foo"; # Apply to foo called in remote shell
}
main;

To enable tracing, set $set_x in the environment of your script to "set -x". To control this on a call-by-call basis, prefix the call with "set_x='set -x'". Because an environment variable is used, this method naturally works with nested calls.

All those uses of "$set_x;" are a bit ugly, but it works with all shells, and the benefit usually outweighs the cost.

Another example of an AIX shell "peculiarity":

set -e;
trap "echo trapped at file scope" EXIT;
f() { return 1; }
main() { f; }
main;

The above should print "trapped at file scope", but prints nothing.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top