I am writing a Bash script for myself to learn scripting. At some point, I need to add trap in order to clean unwanted directories and files if script is killed. However, for some reason I do not understand, trap calls the cleaning function - clean_a()
- when script killed but $LINENO
points to a line in the cleaning function itself, not int the function - archieve_it()
- when script is killed.
Expected behaviour:
- run script
- Press Ctrl+C
- trap caches Ctrl+C and calls
clean_a()
function clean_a()
function echoes the line number, which Ctrl+C is pressed. Let it be line 10 inarchieve_it()
.
What actually happens:
- run script
- Press Ctrl+C
- trap caches Ctrl+C and calls
clean_a()
function clean_a()
echoes an irrelevant line number. Say, line 25 inclean_a()
function.
Here is an example as a part of my script:
archieve_it () {
trap 'clean_a $LINENO $BASH_COMMAND'\
SIGHUP SIGINT SIGTERM SIGQUIT
for src in ${sources}; do
mkdir -p "${dest}${today}${src}"
if [[ "$?" -ne 0 ]] ; then
error "Something!"
fi
rsync "${options}" \
--stats -i \
--log-file="${dest}${rsync_log}" \
"${excludes}" "${src}" "${dest}${today}${src}"
done
}
clean_a () {
error "something!
line: $LINENO
command: $BASH_COMMAND
removing ${dest}${today}..."
cd "${dest}"
rm -rdf "${today}"
exit "$1"
}
P.S.: Original script can be seen here . Definitions and variable names are in Turkish. If it is required, I can translate anything to English.
EDIT: I change the script as best as I can do according to @mikeserv's explanation like this:
#!/bin/bash
PS4='DEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO $BASH_COMMAND'\
SIGHUP SIGINT SIGTERM SIGQUIT
..
}
clean_a () {
error " ...
line: $LINENO $LASTNO
..."
}
Now, if I run script with set -x
and terminate it with Ctrl+C, it prints correct line number as can be seen below:
DDEBUG: 1 : clean_a 1 336 rsync '"${options}"' ...
However, in clean_a()
function, value of $LASTNO
is printed as 1.
line: 462 1
Does it have something to do with the bug that is shown by @Arkadiusz Drabczyk?
EDIT2: I changed script just like the way @mikesrv recommended to me. But $LASTNO returned 1 as the value of the line when script was terminated (it should have been 337).
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
clean_a () {
error " ...
line: $LASTNO $LINENO
..."
} 2>&1
If I run script and terminate it with Ctrl+C while rsync was running, I get this output:
^^MDEBUG: 1 : clean_a '337 1 rsync "${options}" --delete-during ...
...
line: 1 465
As you can see, $LASTNO's value is 1.
While I was trying to figure out what the problem is, I wrote another function - testing
- using parameter substitution format ${parameter:-default}
. So script turned out like this:
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'testing "$LASTNO $LINENO $BASH_COMMAND"'\
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
testing() {
echo -e "${1:-Unknown error!}"
exit 1
} 2>&1
Now, if I run script and press Ctrl+C, I get this output:
^^MDEBUG: 1 : testing '337 1 rsync "${options}" --delete-during ...
337 1 rsync "${options}" --delete-during ...
337 points out the line when I pressed Ctrl+C, while rsync was running.
For another test, I tried writing clear_a
funtion like this:
clear_a () {
echo -e " $LASTNO $LINENO"
}
and $LASTNO still returned 1.
So, this means that we can get correct line number when script terminated if we use parameter substitution?
EDIT3 It seems that I wrongly applied @mikeserv's explanation in EDIT2. I corrected my mistake. Positional parameter "$1
should be replaced with $LASTNO in clear_a
funtion.
Here is the script which works how I want it to work:
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
clean_a () {
error " ...
line: $1
..."
} 2>&1
When the script is terminated, trap
evaluates $LASTNO
- first argument -, $LINENO
- second argument - and $BASH_COMMAND
-third argument -, then pass their values to the clear_a
function. Finally, we print $LASTNO with $1
as the line number which script is terminated.
BASH_LINENO
array which is probably what you want, instead of justLINENO
. – dimo414 Mar 06 '20 at 00:39