0

A script to manage incoming files generates an audit trail that logs transaction sets made up of a header record, several transaction records and a trailer record. A single line is written with delimiters, a string for the header, one each for the transactions and finally the trailer. Its fairly simple and the code snippet below describes it perfectly.

There is, however, a flaw in the ointment, and one that's been around since XP transitioned out: sometimes the id in the transaction has a leading space, and cmd strips this out. dbenham posted its awfulness here and I'm hoping someone might have chanced on a solution to the bug? feature? issue.

The following example shows this, with the first line having leading spaces, and the second replacing the leading and trailing spaces with an underscore by way of contrast, and to see what happens during expansion. The leading space is truncated, the trailing space retained. A delimiter is sent after each string. wrapping the set command in quotes makes no difference.

TEST OUTPUT:

no 1 line:header:leading space:leading trailing spaces  :leadingtrailing pipe :
no 2 line:header:_leading char:_leading trailing chars__:_leadingtrailing pipe_:

SAMPLE CODE:

type nul>file.txt
>>file.txt (<nul set /p x=no 1 line) &&rem first line
>>file.txt (<nul set /p x=:) &&rem delimiter
>>file.txt (<nul set /p x=header)
>>file.txt (<nul set /p x=:)
(<nul set /p x= leading space)1>>file.txt &&rem leading space
>>file.txt (<nul set /p x=:)
(<nul set /p "x= leading trailing spaces  ")1>>file.txt &&rem leading space, trailing space
>>file.txt (<nul set /p x=:)
echo|set /p=" leadingtrailing pipe ">>file.txt
>>file.txt (<nul set /p x=:)
>>file.txt echo:&&rem force a newline to begin line no 2.
>>file.txt (<nul set /p x=no 2 line) &&rem writes to index 1
>>file.txt (<nul set /p x=:)
>>file.txt (<nul set /p "x=header") &&rem no leading, no trailing
>>file.txt (<nul set /p x=:)
(<nul set /p "x=_leading char")1>>file.txt &&rem leading '_'
>>file.txt (<nul set /p x=:)
(<nul set /p "x=_leading trailing chars__")1>>file.txt &&rem leading, trailing '_'
>>file.txt (<nul set /p x=:)
echo|set /p="_leadingtrailing pipe_">>file.txt
>>file.txt (<nul set /p x=:)
rem no newline to avoid a trailing carriage return

CODE EXPANSION: (where the leading white space is stripped out)

(set /p x= leading space 0<nul ) 1>>file.txt  && rem leading space
(set /p "x= leading trailing spaces  " 0<nul ) 1>>file.txt  && rem leading  and trailing spaces

My workaround is to replace any leading spaces in the header id with underscores, and then restore the spaces after the run completes. This works fine because none of the incoming records have an underscore in their id. It would be nice, though, to be able to remove the work-around.

ilinkcs
  • 142
  • 8
  • it's time to move to powershell – phuclv Jul 10 '21 at 15:09
  • `copy /A nul sub.tmp` should create a file with a single end-of-file character (`0x1A`). `for /F %%E in (sub.tmp) do set "_EOF=%%E"` stores it into a variable. Now arbitrary text can be written to another file, like `> text.tmp echo leading and trailing spaces %_EOF%`. This can finally be appended to your log file by `copy /A file.txt + text.tmp file.txt`. Lastly you may clean up the temporary files by `del sub.tmp text.tmp`… – aschipfl Jul 10 '21 at 17:56
  • @aschipfl ~ client choice, not mine – ilinkcs Jul 10 '21 at 18:34
  • @aschipfl , I did try working with temp files as a possible solution, but it proved impractical logistically ~ with such a large set of incoming records. Thank you for the suggestion, though, appreciate it :-) – ilinkcs Jul 10 '21 at 18:57
  • Well, I'm afraid there are no other options to echo arbitrary strings without line-breaks, except to build every line in advance in a variable and echo it (with line-break) once… – aschipfl Jul 10 '21 at 20:46
  • yes, @aschipfl I was afraid that might still be the case. The code I'm using fails in the case of that irritating white space truncation: but the work-around is solid and recovers from it. Thank you for applying your mind to the issue, it's much appreciated :-) – ilinkcs Jul 10 '21 at 22:58

3 Answers3

2

There exists a simple solution with the prompt command, posted by sst at SO: How do I add a space on this line?

@echo off

setlocal
set "prompt=  Hello"
cmd /d /k < nul
echo  World

endlocal
jeb
  • 78,592
  • 17
  • 171
  • 225
  • Thank you @jeb, I I'll give it a whirl with writing out to a file :-) – ilinkcs Jul 12 '21 at 13:53
  • +1 Well I'll be! replacing the cmd line with '>>file.txt cmd /d /k < nul`, putting it it into the line 1 set and the outcome is exactly as required "no 1 line: Hello:header:etc". Totally heroic. You've solved a 9 year puzzle, Jeb! thank you. – ilinkcs Jul 12 '21 at 14:09
  • 1
    @ilinkcs But as mentioned, the honor is due [sst](https://stackoverflow.com/users/4561332/sst) – jeb Jul 12 '21 at 19:53
1

You could use jeb's function based solution here

@echo off
setlocal EnableDelayedExpansion
call :createSub
call :echoWithoutLinefeed "=hello" >"%~dp0outfile.txt"
call :echoWithoutLinefeed " world" >>"%~dp0outfile.txt"
type "%~dp0outfile.txt"
exit /b

:echoWithoutLinefeed
> txt.tmp (echo(%~1!sub!)
copy txt.tmp /a txt2.tmp /b > nul
type txt2.tmp
del txt.tmp txt2.tmp
exit /b

:createSub
copy nul sub.tmp /a > nul
for /F %%a in (sub.tmp) DO (
   set "sub=%%a"
)
del sub.tmp
exit /b

T3RR0R
  • 2,747
  • 3
  • 10
  • 25
  • 2
    Preceding the text with extra characters is only useful for display text but not for being written to a file as all these characters become also written… – aschipfl Jul 10 '21 at 17:58
  • agree, @aschipfl. I did try writing to a temp file back in the day, using the same temp file name, however it did lead to issues ~ about 5000 or so times in a run the file is created and zapped, so that's an overhead, and of course occasionally an administrator revokes read/write access (because they can!) so reverted. Thanks for the link, though, and the sample code, and the energy. As noted many time in the comments, this is not the right tool. But who listens to contractors?! As long as the work-around chugs along, client will leave well be. – ilinkcs Jul 10 '21 at 19:20
0

to amplify jeb's answer (the accepted one) this how his mod is injected into the original Sample code:

setlocal
type nul>file.txt
>>file.txt (<nul set /p x=no 1 line) &&rem first line
>>file.txt (<nul set /p x=:) &&rem delimiter

rem Jeb's mod injected
set "prompt=  Hello"
>>file.txt cmd /d /k < nul
>>file.txt (<nul set /p x=:) &&rem delimiter

rem code continues
>>file.txt (<nul set /p x=header)
>>file.txt (<nul set /p x=:)

type file.txt

endlocal&exit /b

and the result in the file from the type command is:

H>_t

no 1 line:  Hello:header:
H>

which is exactly what is required.

ilinkcs
  • 142
  • 8