Page 1 of 1

function to write any string to a text file without manual escaping

Posted: 28 Jul 2016 17:16
by mirrormirror
sorry for all of the posts today - just asking a bunch of questions that have been on the backburner...

Looking for a function or macro that can safely write all strings to a text file - without having to manually escape characters. For example:

Code: Select all

set "x=1% 2% 3% 4^% 5% ) == & =. a! b! < c! d^! >>e! ^ ^ <<'n1 = n +2 -( )))>>"
ECHO %x%>file.txt
Obviously this doesn't work - and it gets even worse if the string has a double quote (") in the middle somewhere - like this line that I'm writing!

I'm just wondering if this is possible. Currently, if I have a string that may cause problems I use vbscript (script below) and call it like this (but this can be slow to invoke the vbscript engine for a large number of lines):

Code: Select all

CScript //nologo "vbsWriteLine" x


Code: Select all

qt=chr(34):pct=chr(37)
Set wshShell = WScript.CreateObject("WScript.Shell")
'WScript.Echo WScript.Arguments(0)
'WScript.Echo WScript.Arguments(1)

   ArgCntOrig=WScript.Arguments.Count
      IF ArgCntOrig=0 THEN CALL EndScript

   str=""
   'DIM  ArgsInUse
   'REDIM  ArgsInUse(ArgCntOrig-1)
   FullCmdLine=WScript.ScriptFullName
   FOR x=1 to ArgCntOrig -1
      'ArgsInUse(x)=WScript.Arguments(x)
      'FullCmdLine=FullCmdLine & " " & WScript.Arguments(x)
      str=str & wshShell.ExpandEnvironmentStrings(pct & WScript.Arguments(x) & pct)
   NEXT

'str=wshShell.ExpandEnvironmentStrings(pct & WScript.Arguments(0) & pct)
myTxtFile=WScript.Arguments(0)

   x=0
   ' TRIM TABS - SPACES and QUOTES from File Path-Name
DO WHILE x=0
   x=1
   myTxtFile=TRIM(myTxtFile)
   IF LEFT(myTxtFile,1)=vbTAB THEN
      myTxtFile=RIGHT(myTxtFile, LEN(myTxtFile)-1)
      x=0
   END IF
   IF RIGHT(myTxtFile,1)=vbTAB THEN
      myTxtFile=LEFT(myTxtFile, LEN(myTxtFile)-1)
      x=0
   END IF
   IF LEFT(myTxtFile,1)=qt THEN
      myTxtFile=LEFT(myTxtFile, LEN(myTxtFile)-1)
      x=0
   END IF
   IF RIGHT(myTxtFile,1)=qt THEN
      myTxtFile=LEFT(myTxtFile, LEN(myTxtFile)-1)
      x=0
   END IF
LOOP

Dim FSO
Set FSO = CreateObject("Scripting.FileSystemObject")

Set oFile = FSO.OpenTextFile(myTxtFile, 8, True)
oFile.WriteLine(str)

SUB EndScript
      On Error Resume Next
         oFile.Close
      On Error Goto 0
   Set FSO= Nothing
   Set wshShell = Nothing
END Sub

Re: function to write any string to a text file without manual escaping

Posted: 29 Jul 2016 10:57
by penpen
After i've seen the REM ro file trick, i created the $secho macro (loaded calling the "loadSEcho.bat") to do this:
"test.bat"

Code: Select all

@echo off
cls
setlocal
:: load macros $sEcho, $_sEcho
:: occupies filename "_sEcho.txt" in directory "."
call loadSEcho.bat

%$sEcho%(%%a %%b ()[]{}^=;!'+,`~ & |
)

(
%$sEcho%(%%a %%b ()[]{}^=;!'+,`~ & |
)
) > testing.tmp.txt


endlocal
goto :eof




"loadSEcho.bat":

Code: Select all

@echo off
:: define info header
::
   :: This batch file is automatically created using "blockToMacro.bat".
   :: You may find it on "www.dostips.com".

   ::
   :: macro(s) loaded into memory by this batch file:
   ::  - $sEcho
   ::

   ::
   :: supported modes of operation:
   :: ---------------------------------------------+---+---+---+---
   ::   extensions             (e/d: en/disabled)  | e | e | e | d
   ::   delayed expansion      (e/d: en/disabled)  | e | d | d |e/d
   ::   run from command line  (y/n:      yes/no)  |y/n| y | n |y/n
   :: ---------------------------------------------+---+---+---+---
   ::   macro behaviour    (l/e: load/executable)  |l+e| l |l+e| -
   ::

   ::
   :: This batch overwrites the following environment variable names
   :: of the current context, and will finally undefine them:
   ::  - Eo
   ::  - To
   ::  - Ei
   ::  - Ti
   ::

   ::
   :: This macro uses the REM to file trick:
   :: https://www.dostips.com/forum/viewtopic.php?t=2836
   ::
   :: The macro defined here, will overwritte the file ".\_sEcho.txt" without asking.
   ::

   ::
   :: Author of this macro:
   :: penpen from dostips
   ::
::
:: end define info header


:: define check for valid system state
::
:: If the extensions are disabled, the set command throws an "syntax error",
:: when assigning an equal sign ('='):
:: This is essential for complex macros, so the extensions MUST be enabled.
::
2> nul set "extensionsEnabled=true" || (
   echo(Error: The extensions are disabled, no macro will be loaded into memory.

   rem simulate: exit /b 1
   rem if extensions are disabled the label :EOF is not found
   call
   goto exit
)
::
:: end define check for valid system state
:: extensions: enabled


:: define $sEcho
:: Safe echo:
:: Should display anything.
::
:: You have to add a closing round brackets in the next line.
:: The first character after this macro will be ignored,
:: and therefore could be anything:
:: But for optical reasons i recommend to use an opening bracket.
::
:: Test cases:
::
:: %$sEcho%(%%a %%b ()[]{}^=;!'+,`~ & |
:: )
::
:: %$sEcho%&%%a %%b ()[]{}^=;!'+,`~ & |
:: )
::
:: %$sEcho% %%a %%b ()[]{}^=;!'+,`~ & |
:: )
::
::
:: :: outputting to a file:
:: (
:: %$sEcho%(%%a %%b ()[]{}^=;!'+,`~ & |
:: )
:: ) > testing.txt
::
:: Bases on the REM to file trick, see:
:: https://www.dostips.com/forum/viewtopic.php?p=13002#p13002
::
:: The file used for this trick is ".\_sEcho.txt"
:: This file will be overwritten without asking
::
set "Eo=1"
if "!Eo!" == "1" (
   set "Eo=^^^^^^^!"
   set "Ei=^^^!"
   set "To=^^^^"
   set "Ti=^^"
) else (
   set "Eo=!"
   set "Ei=!"
   set "To=^^"
   set "Ti=^"
)

setlocal enableExtensions disableDelayedExpansion
set LF=^


set ^"\n=^^^%LF%%LF%^%LF%%LF%^<nul ^^^"
set ^"[=%%~#^<nul ^^^%LF%%LF%^<nul ^^^"
set ^"]=^<nul^^^%LF%%LF%^%LF%%LF%^<nul ^^^"
endlocal & (
REM ==================== define { // $sEcho ====================


for %%# in ("") do set $sEcho=@for %%a in ^("%%a"^) do @for %%a in ^(%%~a, 2^) do @if %%a == 2 ^(%\n%
   ^(type "_sEcho.txt" ^& del "_sEcho.txt"^) ^| @^(^>nul ^(for /L %%a in ^(1, 1, 10^) do pause^)^&findstr "%Ti%"^)%\n%
^) else ^>"_sEcho.txt" ^(%\n%
   for %%a in ^(1, %%a, 3^) do @if %%a == 1 ^(%\n%
      setlocal disableExtensions%\n%
      set prompt=#%\n%
      echo on%\n%
   ^) else if %%a == 3 ^(%\n%
      echo off%\n%
      endlocal%\n%
   ^) else for %%a in ^(%%a^) do rem .


REM ==================== }        // $sEcho ====================
)
endlocal
set "Eo="
set "Ei="
set "To="
set "Ti="
::
:: end define $sEcho

::
:: All macros loaded to memory: return success (errorLevel == 0).
exit /b 0

:exit


penpen

Re: function to write any string to a text file without manual escaping

Posted: 29 Jul 2016 14:45
by jeb
@penpen
Nice usage of the REM trick.
Something similar is my magicEcho macro
The magicEcho can even handle single percents (no doubling needed).

@mirrormirror
When you only want to echo a variable in a safe way, this is really easy with delayed expansion.
Delayed expansion is always safe against any special character, as after the delayed expansion no more parser phases occours.

Code: Select all

set "x=1% 2% 3% 4^% 5% ) == & =. a! b! < c! d^! >>e! ^ ^ <<'n1 = n +2 -( )))>>"
>file.txt ECHO(!x!

The echo( is only to protect the echo command against content like "ON" or "/?"

Re: function to write any string to a text file without manual escaping

Posted: 29 Jul 2016 15:50
by mirrormirror
One of the issues I run across is that I can read text lines from files fine but sometimes can't write out those same lines. I tried your example using this string but get the error you see in the output below:

Code: Select all

@ECHO OFF &SETLocal DISABLEDelayedExpansion

FOR /F "tokens=* delims=" %%a IN ('FINDSTR /R /C:"^" t.txt') DO (
   set str=%%a
   REM @ECHO %%a
   SETLocal ENABLEDelayedExpansion
   @ECHO !str!
   @ECHO sending output -------------------------
   t2.txt>ECHO(!str!
   ENDLocal
)

Output:

Code: Select all

T:\>mtest2
1% 2% 3% 4^% 5% ) == & =. a! b! c! "text" " d^! e! ^ ^ 'n1 = n +2 -( )))\|t:\t3.txt| >>t4.txt
sending output -------------------------
The filename, directory name, or volume label syntax is incorrect.

t.txt contains only one line with the text:
1% 2% 3% 4^% 5% ) == & =. a! b! c! "text" " d^! e! ^ ^ 'n1 = n +2 -( )))\|t:\t3.txt| >>t4.txt
and the file t2.txt never gets created.

penpen - thank you for the code - I'll need to look at it later and try it out (jeb's was much shorter and easier to digest - but not his magicecho :))

Re: function to write any string to a text file without manual escaping

Posted: 29 Jul 2016 15:58
by jeb
Simple fix it:

Code: Select all

>t2.txt ECHO(!str!

First the redirect character then the filename

Re: function to write any string to a text file without manual escaping

Posted: 29 Jul 2016 16:06
by mirrormirror
First the redirect character then the filename

:oops: - maybe I have the onset of dyslexia !
Thank you - this is simple and easy. I thought certain characters would cause issues with redirection to a file but I guess I am wrong. Maybe I am getting redirect confused with pipe...

Re: function to write any string to a text file without manual escaping

Posted: 29 Jul 2016 16:30
by mirrormirror
And to add an example of where I had a prior problem here:
http://www.dostips.com/forum/viewtopic.php?f=3&t=7291

I suppose I could simply echo out each line seperately (without parentheses) and not have to worry about escaping my parentheses "^)"

I'll try it next time