I have a bunch of scripts that I am debugging, all nested and quite nasty.
Just wondering if I'm able to set some environment variable that is equivalent of the -x option to bash. This will save me amazing amounts of time.
I have searched for the answer, but seems it doesn't exist - hoping you smart folks might be able to offer advice, or perhaps an alternative solution.
Thanks!
Because you seem like you're in dire straits, I'll suggest some horrible options (and one option that sounds horrible, but isn't so bad).
First choice: use a different tool. strace(1)
and ltrace(1)
can both give an amazing array of information -- though neither one understand anything about the shell variables, they will show you how the scripts interact with the rest of the system and some of the internal states of the program. It might be good enough. (If you're new to strace(1)
, try strace -f -o /tmp/foo ./program
-- it'll follow fork(2)
, vfork(2)
, and clone(2)
calls, so child processes are followed and dumped too. Output goes to /tmp/foo
.)
Second choice: replace all the #!/bin/bash
with #!/bin/bash -x
in all your scripts:
find . -type f -print0 | xargs -0 sed -i -e 's/^#!\/bin\/bash$/#!\/bin\/bash -x/'
Perhaps there's a better mechanism for replacing the shebang line on all your scripts, but this feels alright. It'll miss all the uses of system(3)
, popen(3)
, etc., in C programs called via your scripts, but that might be just fine. If any scripts rely on arguments to /bin/bash
, you may need to work harder to add -x
properly.
Very last choice: start a sash(1)
shell as root. Copy /bin/bash
to /bin/real.bash
. Write a quick-and-dirty C program that will add -x
to the command line arguments to /bin/real.bash
, and place it into /bin/bash
. This will get everything: every system(3)
, every popen(3)
, every init script, all cron jobs, all user logins, everything.
You could modify that very last choice a little bit; the Linux kernel provides per-process private namespaces that can be used to make a new /bin/bash
visible to just children of a given process. It's a little more complicated...
bash
: cp /bin/bash /bin/real.bash
-x
to the command line arguments and call /bin/real.bash
. (Lets call it /bin/wrapper.bash
.)/
mount shared
: mount --make-shared /
unshare --mount bash
bash
, make /
a slave: mount --make-slave /
bash
, bind-mount your replacement bash
: mount -B /bin/wrapper.bash /bin/bash
bash
, start your shell scripts. They will all be redirected to your wrapper. Processes not descended from the new bash
will continue to use the "real" /bin/bash
, which remained unmodified.When the last (grand)*child process dies, so does the private namespace and the funny bind mount.
The upshot of all this funny business is that all your shell scripts remain unchanged and you get to use the debugging tool you thought would be most useful. If you squint your eyes just right, it isn't very intrusive.
I performed many of these steps on my workstation (except I used --make-rshared
and --make-rslave
instead, and tested with /bin/dir
and /bin/ls
instead of /bin/bash
), and checked the inode numbers with ls -li /bin/dir /bin/ls
from shells within the new namespace and outside the new namespace, and processes outside the namespace continued to see the inode numbers remain unchanged but processes within the namespace saw dir
and ls
share inode numbers.
For full details of the namespaces, private mounts, and bind mounts, I recommend reading the Documentation/filesystems/sharedsubtree.txt
file from the kernel source tree, the clone(2)
manual page, the unshare(1)
manual page, and the mount(8)
manual page.
Bind mounts go away on system reboots, so you can't screw anything up too badly. :)
No, it's not controlled by an environment variable, but you can do set -x
where you need to, and set +x
to turn it back off.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With