KornShell - Set "-x" (debug) flag globally?
Asked Answered
P

5

9

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->
Pierpont answered 16/2, 2010 at 14:16 Comment(2)
That's weird. I also just tried it in ksh88 (and ksh93) on one of my AIX systems, and I do get the behvior described. But the ksh88 on my HP-UX systems works like you want. I modified my answer to reflect the results.Bonus
fyi, ksh88 /usr/bin/ksh on Solaris 10 also shows the described behaviour (no global effect set -x).Byssus
B
8

This is ksh88 on an HP-UX machine:

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

hello() {
  print $1
}

hello kapow!
exit

[email protected]/
$ ./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. :(

Bonus answered 16/2, 2010 at 17:5 Comment(2)
Thanks for the great research, I'll see about getting upgraded to ksh93, but in my environment ...Pierpont
IBM provides ksh93 and ksh88 in the same package (at least they do on the AIX 5.3 and 6.1 systems that I have looked at); the binary is just called ksh93. If that helps... :)Bonus
B
6

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.

Byssus answered 14/4, 2013 at 20:17 Comment(1)
+1 this worked for me using ksh88 on AIX 5.3 and 7.1. It would be nice if there was more of an explanation of how typeset works.Propellant
S
4

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
Schear answered 16/2, 2010 at 14:31 Comment(1)
Unfortunately none of these work for me. I see only the names of the methods. It may be because I'm using ksh88.Pierpont
A
1

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

Arcane answered 5/8, 2011 at 0:41 Comment(0)
T
1

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.

Tridentum answered 3/2, 2012 at 16:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.