Page 1 of 1

Simple log function does not work with some characters (such as parentheses or variables)

Posted: 13 Mar 2019 01:59
by Xandr0s
Hello,

I am currently working on a script in which I would like to set up a log recording function.

But my function doesn't work well. Depending on what I send, some characters (such as parentheses or variables) crash my script.

I think this comes from having set a condition to check whether or not the recording is enabled. Because without it, it works, but I want to keep it.

Here is a part of my script with some examples of what works and what doesn't:

Code: Select all

@echo off

setlocal EnableDelayedExpansion

set "Log_record=yes"
set "Log_folder=D:\Logs\"

for /f "delims=" %%G in ('time /t') do set heure=%%G

echo %date%>len_date
for %%a in (len_date) do set /a len_date=%%~za -2

REM English OS, also European
if !len_date! gtr 10 (
	set datetime=%date:~-4%-%date:~4,2%-%date:~7,2%_%heure:~0,2%-%time:~3,2%-%time:~6,2%
) else (
	set datetime=%date:~-4%-%date:~3,2%-%date:~0,2%_%heure:~0,2%-%time:~3,2%-%time:~6,2%
)

set "Log_name=Log_%computername%_!datetime!.txt"

for /f %%A in ('whoami') do set "currend_user=%%A"

REM Work
call :Write_Log - [Error]	- The script isn't running under an administrator account or there has been no increase privileges ^^^("Run as Administrator", from Windows Vista^^^).

REM Work
call :Write_Log - [Error]	- The script wasn't executed under the account : "!currend_user!".

REM Don't work, error : ". était inattendu." in french, like : ". was unexpected."
call :Write_Log - [Info]	- The script was executed under the account: "!currend_user!" ^^^("Run as Administrator", from Windows Vista^^^).

goto :END

:Write_Log

	if "!Log_record!" == "yes" (
		echo %date% !heure!%time:~5,3% %* >> "!Log_folder!!Log_name!"
	)

	goto :eof

:END
	call :Write_Log - [Info]	- End of script execution.
	pause

I also tried to retrieve the function settings with "$~1" and put the text between quotes, but it doesn't work any better.

Do you have any advice to give me to help me?

With kind regards.

Re: Simple log function does not work with some characters (such as parentheses or variables)

Posted: 13 Mar 2019 06:58
by penpen
I think the number of circumflex accents is wrong. so the following might work (currently untested, posting from my mobile phone):

Code: Select all

call :Write_Log - [Info]	- The script was executed under the account: "!currend_user!" ^^^^^("Run as Administrator", from Windows Vista^^^^^).
penpen

Re: Simple log function does not work with some characters (such as parentheses or variables)

Posted: 13 Mar 2019 07:17
by Xandr0s
Thank you penpen for your help.

I tried with four carets, but not five.

It works :).

But, why so many carets ?
Otherwise, does my function seem correct to you ?

Re: Simple log function does not work with some characters (such as parentheses or variables)

Posted: 13 Mar 2019 08:37
by dbenham
First off, here is the minimum number of carets needed to get your existing code to work:

Code: Select all

REM Line without delayed expansion
call :Write_Log - [Error] - The script isn't running under an administrator account or there has been no increase privileges ("Run as Administrator", from Windows Vista^^).

REM Line with delayed expansion
call :Write_Log - [Info]  - The script was executed under the account: "!currend_user!" ("Run as Administrator", from Windows Vista^^^^).
So you may ask, "That makes no sense :? What is going on :?: "

It is actually very predictable if you understand how the batch parser works. If you follow that link, the relevant information is buried in phases 2 and 5 (and phase 6 has the potential to add to the complexity)

First off, a ( literal only needs to be escaped if it precedes a command. That is never the case in any of your examples, so it never needs to be escaped.

The ) literal only needs to be escaped when it appears inside a parenthesized block of code. This is not the case in your CALL, but it is the case inside your :Write_Log subroutine. So this means any ) literal within %* must be escaped as ^) for the ECHO statement's phase 2. The trick is how to get that correct syntax to the subroutine.

In the first CALL without delayed expansion, you only need to worry about Phase 2. You need ^^) so that phase 2 changes the value to ^), and then the subroutine's phase 2 reduces the value to the desired ).

In the second CALL with delayed expansion, phase 5 needs the ^ to be escaped as ^^. So you need to start with ^^^^). The CALL statement phase 2 reduces that to ^^), and then phase 5 reduces that to ^), and finally the subroutine phase 2 reduces that to the desired ).

The reason I mentioned phase 6 is because it plays games with ^ characters. It doubles all ^ as ^^, but then restarts an additional round of phase 2 which reduces it back to ^ as long as the character is not quoted. But if the character is quoted, then it remains doubled, and there is nothing you can do about it. It is impossible to pass a single quoted ^ literal through a CALL statement - it will always be doubled. Thankfully, that is not a concern in your code.

But your code can easily be simplified so that you need not escape any characters :!:

Parentheses are mostly useful when you want to execute multiple commands within an IF or FOR statement. But your IF statement is only executing a single command. So if you rewrite the subroutine as:

Code: Select all

:Write_Log
  if "%Log_record%" == "yes" echo %date% !heure!%time:~5,3% %* >> "!Log_folder!!Log_name!"
  exit /b
then you can simply use:

Code: Select all

call :Write_Log - [Error] - The script isn't running under an administrator account or there has been no increase privileges ("Run as Administrator", from Windows Vista).
call :Write_Log - [Info]  - The script was executed under the account: "!currend_user!" ("Run as Administrator", from Windows Vista).
One other note, you are using delayed expansion without any purpose - there is no need for delayed expansion in your code.

Delayed expansion serves two purposes in batch code:

1) It allows you to work with poison characters without worrying about escaping because the expansion occurs after phase 2, where poison characters are detected. So if you were to do something like ECHO !VAR! then you can be sure that the value of VAR will be preserved, regardless of the content.

2) It allows you to access the value of a variable that was set within the same code block. An entire block of code is parsed through phases 1 and 2 in one step, so normal expansion will show the value that existed before the block was entered. Below is a simple demonstration:

Code: Select all

@echo off
setlocal enableDelayedExpansion
set "var=BEFORE"
(
  set "var=AFTER"
  echo Percent Expansion yields %var%
  echo Delayed Expansion yields !var!
)
--OUTPUT--

Code: Select all

Percent Expansion yields BEFORE
Delayed Expansion yields AFTER
Neither of the above conditions exist in your code, so you can safely use normal percent expansion in all cases, and you don't need to enable delayed expansion.


Dave Benham

Re: Simple log function does not work with some characters (such as parentheses or variables)

Posted: 13 Mar 2019 09:57
by Xandr0s
Hello Dave,

Thank you very much for your explanations and the link. It will give me a headache :), but this is very instructive.

I take note of your comments and I will adapt my script.

Regarding the delayed expansion, this was only part of my script. I simply illustrated my problem to write my log file ;).

However, to continue the delayed expansion, I should only set exclamation marks for variables that are between condition (if) or loop (for) brackets and if the value of the variable is changed in it. As explained in Phase 5. Is that correct?

Re: Simple log function does not work with some characters (such as parentheses or variables)

Posted: 13 Mar 2019 10:32
by dbenham
Once you enable delayed expansion, it is often advisable to use it through out, and it certainly doesn't hurt to use it.

The reason why it may be wise to use delayed expansion if it is enabled is because ! literals within variables will need to be escaped if you use percent expansion while delayed expansion is enabled.


Dave Benham

Re: Simple log function does not work with some characters (such as parentheses or variables)

Posted: 15 Mar 2019 04:40
by Xandr0s
Hello Dave,

Thank you very much for your help and advice.

Have a good day.