Say I have a script that is reading from a file being printed to. In other words, I have Script A running, which prints its progress in a percentage i.e. 40%. Script A does this until 100% is reached. The file name being printed to is File_A. Script B looks like the following:
$pid=script A process ID
trap "kill $pid 2> /dev/null" EXIT
percent=$(cat File_A | tail -n 1)
while kill -0 $pid 2> /dev/null ; do printf "\r%s" "$percent" ; sleep 1 ; ((n )) ; done
The result I get from Script B is a line that prints the same percentage; the percentage doesn't change. For example, the terminal looks like so: 40%
It stays at 40%(an arbitrary number I picked for the sake of using an example). EVEN THOUGH Script A is still running AND printing to File_A; the percent in the file is updating, but Script B won't print these new lines.
I am not sure how to get Script B to print the updated lines in File_A. I'm assuming it has something to do with different shell sessions, but I wouldn't know exactly what question to ask in regards to that; So how can I solve this?
CodePudding user response:
The primary problem here is that you set percent
once at the beginning, and never update it. You'd need to put percent=$(cat File_A | tail -n 1)
inside the loop to get it to update each time.
But there's a second problem, which won't prevent it from working, but makes it inefficient (especially if it runs that command over and over and over). The construct cat somefile | somecommand
is what's sometimes called a "useless use of cat
", because cat
isn't doing anything useful here -- the next program in the pipe can read from the file perfectly well by itself, it doesn't need cat
to preprocess it. In this case, using cat
here isn't just useless, it makes tail
less efficient. Compare these two commands:
tail -n 1 File_A # tail reads directly from File_A
cat File_A | tail -n 1 # tail reads from a pipe from cat
In the first version, since tail
has direct access to the file, it can basically start reading the file from the end until it has the last line, print that, and be done. In the version with cat
, it cannot do this, since cat
sends the file in order. So in the second version, tail
(and cat
) must read through the entire file, just to ignore all but the last line.
So this would be a much better way to do it:
n=1
while [ $n -le 100 ] ; do
percent=$(tail -n 1 File_A)
printf "\r%s" "$percent"
sleep 1
((n ))
done
But there's another method that might work better. Rather than re-checking the file constantly, you could use tail -f
to "follow" the file and get updates when it changes. However, there's a difficulty here that it doesn't know when to stop reading (i.e. when the output has finished), so you have to figure out how to detect that and exit the loop. Something like this might work:
tail -n 1 -f File_A | while read percent; do
printf "\r%s" "$percent"
[[ "$percent" = "100" ]] && break
done
(Note that if the actual final output is something like "100%" instead of just "100", you'd have to change that comparison appropriately.)