Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash environment variable that is equivalent to -x?

Tags:

bash

shell

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!

like image 700
MediumDave Avatar asked Dec 28 '22 13:12

MediumDave


2 Answers

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...

  1. Make a copy of bash: cp /bin/bash /bin/real.bash
  2. Write your C wrapper to add -x to the command line arguments and call /bin/real.bash. (Lets call it /bin/wrapper.bash.)
  3. Make the / mount shared: mount --make-shared /
  4. Create a new filesystem namespace: unshare --mount bash
  5. Within the new bash, make / a slave: mount --make-slave /
  6. Within the new bash, bind-mount your replacement bash: mount -B /bin/wrapper.bash /bin/bash
  7. Within the new 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. :)

like image 177
sarnold Avatar answered Jan 15 '23 18:01

sarnold


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.

like image 22
tripleee Avatar answered Jan 15 '23 18:01

tripleee