TL;DR: Is there a way to print the inputs and outputs of every step of a bash shell script, including the steps between pipe commands?
Hopefully this is relatable: I wrote a script a while ago and now want to edit it, but I don't understand how it works anymore. I'll use this script as an example:
#! /usr/bin/bash
set -ev
TREE_IGNORE_PATTERN='node_modules|package-lock.json|.gitignore|.git|dist'
echo '### Project Structure'
echo '```'
tree --noreport -F -a -I $TREE_IGNORE_PATTERN | tr -d '*'
echo '```'
echo ""
tree --noreport -nFif -I $TREE_IGNORE_PATTERN | awk '$0 !~ /\/$/ && $0 !~ /^\.$/' | tr -d '*' | awk '{print "" ; print "### " $0 ; print "```" ; system("cat " $0 ) ; print "" ; print "```" ; print "" }'
As you can see, I set -v
at the top. This helps because it tells me what's actually executing, but it's not enough to understand how each step of the pipes work. What would really help is seeing the input transform step-by-step. Since this is so informative, I normally go about it manually:
- I forget how
tree --noreport -nFif -I $TREE_IGNORE_PATTERN | awk '$0 !~ /\/$/ && $0 !~ /^\.$/' | tr -d '*' | awk '{print "" ; print "### " $0 ; print "```" ; system("cat " $0 ) ; print "" ; print "```" ; print "" }'
works, so I would copy it. - I paste it into my commandline.
- Since it depends on a variable named
$TREE_IGNORE_PATTERN
:- I copy the expanded value of that variable.
- I replace that variable on the command line with the expanded value.
- Now the command looks like this:
tree --noreport -nFif -I 'node_modules|package-lock.json|.gitignore|.git|dist' | ...
.
- I press enter on the command line to run step 3.3. This is to save the command in my history so I can get back to it by pressing up.
- I press up.
- I edit the command line by removing all piped commands, leaving me with
tree --noreport -nFif -I 'node_modules|package-lock.json|.gitignore|.git|dist'
. - I run that to get a sense of the output.
- I press up.
- I edit the command line by removing all piped commands but 1, leaving me with
tree --noreport -nFif -I 'node_modules|package-lock.json|.gitignore|.git|dist' | awk '$0 !~ /\/$/ && $0 !~ /^\.$/'
. - I run that to get a sense of the output.
- I repeat until I'm running the step 3.3 on the command line again. Now I can see every step of the input's transformation.
This is a lot of manual work. Is there an automated way of seeing the input and output of each pipe command in a bash shell script?
If the answer is, "No, but zsh
can," I'd accept that as an answer (but only if bash
can't). I'd also accept, "No, but if you wrote/commented your code like this, it would be easier to understand when you revisit it," but again, only if bash
and zsh
don't have a way to automate this.
set -v
just print the input lines as-is, it doesn't really show what the shell does. On the other hand,set -x
would show the commands as it runs them, though not the exact source command line that produces them.man
say page seems to say that, but when I used it, the only difference was when say the commands are shown.-x
say shows when they are run, I can't remember when-v
does, unfortunately. The explanation was elsewhere on this site.bash -vc 'for x in 1 2 3; do echo $x; done'
prints that whole line, once, and nothing more, while the same with-x
instead prints each ofecho 1
,echo 2
,echo 3
(plus a line for thefor
itself) while the loop runs. The output is different, but yes, of course it shows in the timing too. It's even more evident if you add asleep 1
to the loop.man
meant by "input lines". You're right. FWIW, I use-x
instead of-v
most of the time.