Shell Script function to use script name for log file output

Hi Team -

I"m very new to Shell Scripting so I have a rather novice question. My forte is Windows Batch Scripting so I was just wondering what the Shell Script equivalent is to the DOS command %~n?

%~n is a DOS variable that dispayed the script name.

For instance (in DOS):

REM ------------------------------------------------------------------------
REM SET LOG File Path
REM ------------------------------------------------------------------------

SET intrapath=%MAINPATH%%LOGPATH%
SET errorintrapath=%MAINPATH%%ERRORPATH%

FOR /f "tokens=1-2 delims=/:" %%a in ("%TIME%") do (set timestamp=%%a%%b)
FOR /f "tokens=* delims= " %%c in ("%timestamp%") do (set timestamp=%%c)

SET exportfile=%exportintrapath%%~n0.csv

SET logfile=%intrapath%%date:~-4,4%%date:~-10,2%%date:~-7,2%_%timestamp%_%~n0.log

Thanks!

---------- Post updated at 02:36 PM ---------- Previous update was at 02:08 PM ----------

Would this be the equivalent?

$(basename $BASH_SOURCE)

or $0

? Thanks!

$(basename $BASH_SOURCE) 

This will do what you want however, it is limited to the bash shell. If this is not a problem for you, feel free to use this if you want.

$0

This will display the full file name of the current script being ran and this special variable will work in both ksh and bash. In order to get the script name on its own, you can use the basename command again or simply use parameter expansion like so:

"${0##*/}"
1 Like

THank you very much!

So, below is my original:

#!/bin/bash
#:: ------------------------------------------------------------------------
#:: -- SCRIPT NAME: fdmee_act_load.sh
#:: -- 
#:: -- DESCRIPTION: This script executes the FDMEE runbatch utility
#::                 To copy actuals to target intersections 
#:: -- 
#:: -- PARAMETERS:  Call setenv.cmd to get environment variables to determine 
#::                 login info, database, application, etc.
#:: --        
#::  Author:        
#::  Date:            04/02/16
#:: ------------------------------------------------------------------------
source /u01/hyp_app/scripts/setenv.sh

#:: SET LOG & ERROR FILES

_INTRA_PATH=$_MAIN_DIR/$_LOG_DIR/$_FDMEE_LOG_DIR
_ERROR_INTRA_PATH=$_MAIN_DIR/$_ERROR_DIR/$_FDMEE_ERROR_DIR

_LOGFILE=$_INTRA_PATH/$_DATETIMESTAMP_fdmee_act_load.log

And this this would be with your suggestion:

#!/bin/bash
#:: ------------------------------------------------------------------------
#:: -- SCRIPT NAME: fdmee_act_load.sh
#:: -- 
#:: -- DESCRIPTION: This script executes the FDMEE runbatch utility
#::                 To copy actuals to target intersections 
#:: -- 
#:: -- PARAMETERS:  Call setenv.cmd to get environment variables to determine 
#::                 login info, database, application, etc.
#:: --        
#::  Author:        
#::  Date:            04/02/16
#:: ------------------------------------------------------------------------
source /u01/hyp_app/scripts/setenv.sh

#:: SET LOG & ERROR FILES

_INTRA_PATH=$_MAIN_DIR/$_LOG_DIR/$_FDMEE_LOG_DIR
_ERROR_INTRA_PATH=$_MAIN_DIR/$_ERROR_DIR/$_FDMEE_ERROR_DIR

_LOGFILE=$_INTRA_PATH/$_DATETIMESTAMP_${0##*/}.log

So ${0##*/} = fdmee_act_load   ?

Please use the code tags.

If the script name is fdmee_act_load.sh as it is mentioned in your comment, then no. The value of "${0##*/}" would be fdmee_act_load.sh. In order to strip the ".sh", you would need to use a temporary variable, for example:

tmp="${0##*/}"
## Now ${tmp%%.sh*} equals fdmee_act_load providing the script name is fdmee_act_load.sh*
1 Like

Pilnet -

Okay great thank you! So my script would then look like the following:

#!/bin/bash
#:: ------------------------------------------------------------------------
#:: -- SCRIPT NAME: fdmee_act_load.sh
#:: -- 
#:: -- DESCRIPTION: This script executes the FDMEE runbatch utility
#::                 To copy actuals to target intersections 
#:: -- 
#:: -- PARAMETERS:  Call setenv.cmd to get environment variables to determine 
#::                 login info, database, application, etc.
#:: --        
#::  Author:        Chris Takacs (Peloton Group)
#::  Date:            04/02/16
#:: ------------------------------------------------------------------------
source /u01/hyp_app/scripts/setenv.sh

#:: SET FILE NAME
_FN=${0##*/}

#:: SET LOG & ERROR FILES

_INTRA_PATH=$_MAIN_DIR/$_LOG_DIR/$_FDMEE_LOG_DIR
_ERROR_INTRA_PATH=$_MAIN_DIR/$_ERROR_DIR/$_FDMEE_ERROR_DIR

_LOGFILE=$_INTRA_PATH/$_DATETIMESTAMP_${_FN%%.sh*}.log

Is that correct?

Yes exactly that should work as you expect.

If you don't want to set the file name/use the temp variable, you can use the basename command which you mentioned in the original post. It will invoke a subshell but it will run on one line. So it would look like the below:

_LOGFILE=$_INTRA_PATH/$_DATETIMESTAMP_$(basename ${_0%%.sh*}).log

Simply comes down to preference, both methods will work.

Also just to let you know, a single period '.' is a synonym for the 'source' built-in, both do the same thing, in case you want to use that as well - again down to preference. I would also recommend enclosing your variables in curly braces, it is good practice and looks a lot cleaner :slight_smile:

1 Like

Pilnet -

Very much appreciated - thank you very much!

Also, would this version work too? Or am I still missing something?

#!/bin/bash
#:: ------------------------------------------------------------------------
#:: -- SCRIPT NAME: fdmee_act_load.sh
#:: -- 
#:: -- DESCRIPTION: This script executes the FDMEE runbatch utility
#::                 To copy actuals to target intersections 
#:: -- 
#:: -- PARAMETERS:  Call setenv.cmd to get environment variables to determine 
#::                 login info, database, application, etc.
#:: --        
#::  Author:        Chris Takacs (Peloton Group)
#::  Date:            04/02/16
#:: ------------------------------------------------------------------------
source /u01/hyp_app/scripts/setenv.sh

#:: SET FILE NAME

_SCRIPTNAME="fdmee_act_load.sh"
echo "SCRIPT NAME: ${_SCRIPTNAME%.*}"

#:: SET LOG & ERROR FILES

_INTRA_PATH=$_MAIN_DIR/$_LOG_DIR/$_FDMEE_LOG_DIR
_ERROR_INTRA_PATH=$_MAIN_DIR/$_ERROR_DIR/$_FDMEE_ERROR_DIR

_LOGFILE=$_INTRA_PATH/$_DATETIMESTAMP_${_SCRIPTNAME%.*}.log

As there are no periods in your script name apart from the suffix, yes this will work. Obviously as you have seen though, there is no need to hard code your script name within your script itself. I would definitely recommend enclosing your variables within curly braces as I mentioned in the previous post. For example: ${_INTRA_PATH}

Pilent -

Okay thank you. Sorry, i'm very new to shell scripting. What is the advantage of the curly braces? And Should each variable receive them?

For instance:

_LOGFILE=${_INTRA_PATH}/${_DATETIMESTAMP}_${_SN%%.sh*}.log

Apart from looking a lot cleaner and making your code much more readable, enclosing your variable names with curly braces is good practice as you are likely to encounter situations where curly braces are required, for example:

  • Referencing positional parameters beyond 9 (e.g. ${10} not $10)
  • Utilising parameter expansion
  • Expanding arrays (e.g. ${ARRAY[5]})

Most importantly, in your code your "$_LOGFILE" variable will actually break unless you enclose your "$DATETIMESTAMP" variable with curly braces (as you have done in your recent post). Otherwise it will try to find a variable called "$_DATETIMESTAMP" which does not exist.

Pilent -

So best practice is to enclose every variable like that? Awesome! Thank you very much, I'll adjust all my scripts now.

Thank you again for all your help!

Yes I would say so and no problem.