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 scriptPress Ctrl+Ctrap caches Ctrl+C and calls clean_a() functionclean_a() function echoes the line number, which Ctrl+C is pressed. Let it be line 10 in archieve_it(). What actually happens:run scriptPress Ctrl+Ctrap caches Ctrl+C and calls clean_a() functionclean_a() echoes an irrelevant line number. Say, line 25 in clean_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/bashPS4='DEBUG: $((LASTNO=$LINENO)) : '; set -xarchieve_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 1Does 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/bashPS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -xarchieve_it () {  trap 'clean_a $LASTNO $LINENO $BASH_COMMAND' \                SIGHUP SIGINT SIGTERM SIGQUIT  ...} 2>/dev/nullclean_a () {  error  ...  line: $LASTNO $LINENO  ...} 2>&1If 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 465As 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/bashPS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -xarchieve_it () {  trap 'testing $LASTNO $LINENO $BASH_COMMAND'\                 SIGHUP SIGINT SIGTERM SIGQUIT  ...} 2>/dev/nulltesting() {  echo -e ${1:-Unknown error!}  exit 1} 2>&1Now, 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/bashPS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -xarchieve_it () {  trap 'clean_a $LASTNO $LINENO $BASH_COMMAND' \                SIGHUP SIGINT SIGTERM SIGQUIT  ...} 2>/dev/nullclean_a () {  error  ...  line: $1  ...} 2>&1When 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.
Getting wrong $LINENO for a trapped function
bash;trap
I think the problem is you're expecting $LINENO to give you the line of execution for the last command which might almost work, but clean_a() also gets its own $LINENO and that you should do instead:error something!line: $1...But even that probably wouldn't work because I expect it will just print the line on which you set the trap.Here's a little demo:PS4='DEBUG: $LINENO : ' \bash -x <<\CMD              trap 'fn $LINENO' EXIT                 fn() { printf %s\\n $LINENO $1; }    echo $LINENOCMDOUTPUTDEBUG: 1 : trap 'fn $LINENO' EXITDEBUG: 3 : echo 33DEBUG: 1 : fn 1DEBUG: 2 : printf '%s\n' 2 121So the trap gets set, then, fn() is defined, then echo is executed. When the shell completes executing its input the EXIT trap is run and fn is called. It is passed one argument - which is the trap line's $LINENO. fn prints first its own $LINENO then its first argument.I can think of one way you might get the behavior you expect, but it kinda screws up the shell's stderr:PS4='DEBUG: $((LASTNO=$LINENO)) : ' \bash -x <<\CMD    trap 'fn $LINENO $LASTNO' EXIT    fn() { printf %s\\n $LINENO $LASTNO $@; }    echo $LINENOCMDOUTPUTDEBUG: 1 : trap 'fn $LINENO $LASTNO' EXITDEBUG: 3 : echo 33DEBUG: 1 : fn 1 3DEBUG: 2 : printf '%s\n' 2 1 1 32113It uses the shell's $PS4 debug prompt to define $LASTNO on every line executed. It's a current shell variable which you can access anywhere within the script. That means that no matter what line is currently being accessed you can reference the most recent line of the script run in $LASTNO. Of course, as you can see, it comes with debug output. You can push that to 2>/dev/null for the majority of the script's execution maybe, and then just 2>&1 in clean_a() or something.The reason you get 1 in $LASTNO is because that is the last value to which $LASTNO was set because that was the last $LINENO value. You've got your trap in the archieve_it() function and so it gets its own $LINENO as is noted in the spec below. Though it doesnt appear that bash does the right thing there anyway, so it may also be because the trap has to re-exec the shell on INT signal and $LINENO is therefore reset. I'm a little fuzzy on that in this case - as is bash, apparently.You don't want to evaluate $LASTNO in clean_a(), I think. Better would be to evaluate it in the trap and pass the value trap receives in $LASTNO through to clean_a() as an argument. Maybe like this:#!/bin/bashPS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -xarchieve_it () {    trap 'clean_a $LASTNO $LINENO $BASH_COMMAND' \        SIGHUP SIGINT SIGTERM SIGQUIT    while :; do sleep 1; done} 2>/dev/nullclean_a () { : $@ ; } 2>&1Try that - it should do what you want, I think. Oh - and note that in PS4=^M the ^M is a literal return - like CTRL+V ENTER.From the POSIX shell spec:Set by the shell to a decimal number representing the current sequential line number (numbered starting with 1) within a script or function before it executes each command. If the user unsets or resets LINENO , the variable may lose its special meaning for the life of the shell. If the shell is not currently executing a script or function, the value of LINENO is unspecified. This volume of IEEE Std 1003.1-2001 specifies the effects of the variable only for systems supporting the User Portability Utilities option.