Retrieve data from NTFS alternative data stream - cmd

I have an ADS in a file:
17/03/2021 17:27 100 test2.txt
10 test2.txt:1:$DATA
1 File(s) 100 bytes
0 Dir(s) some bytes free
which I got by type somefile > test2.txt:1 resulting in 10 byte ADS correctly.
But when I DonaldTrump it from ADS to separate file, I get extra 2 bytes (0d0a):
findstr /V /L W3AllLov3DonaldTrump <test2.txt:1>test.exe
How do I avoid this (and get a 10 byte file like the original instead of a 12 byte file)?

It's findstr, which adds the line ending (CRLF, 0d0a). Same with find, type, and more. So you can't use one of those commands. Copy /b would be a solution, but it doesn't accept ADS-names.
If there is just one line in the ADS (which I assume it is):
set /p "x="<"test2.txt:1"
<nul set /p "=%x%" >test.exe
Note: there is a length limit with the set command and I'm not sure how that reacts to non-printable characters (especially NUL (0x00)) (which are quite common with anything, I'd expect in an .EXE), so this might be of limited usage.

Related

Batch combine new elements into subtitle files

I want to take a folder with several (15 in this first case) 'subtitle.srt' files (which, as I'm sure you're aware, are just text files with the extension ".srt" instead of ".txt") and modify each file in turn so that a new subtitle is added at the start of each file.
I want the new subtitle to be:-
0
00:00:00,001 --> 00:00:02,100
"Filename"
So, for example, if the first subtitle file in the folder is called "01. What Lies Beneath.srt" and it looks like this:-
1
00:00:02,120 --> 00:00:03,560
<font color="#FFFF00">Previously on Superman & Lois...</font>
2
00:00:03,560 --> 00:00:06,880
<font color="#00FFFF">I'm stepping down from active duty.</font>
<font color="#00FF00">You're going to be hard to replace.</font>
3
Etc., etc...
then after processing, I want it to look like this:-
0
00:00:00,001 --> 00:00:02,100
01. What Lies Beneath
1
00:00:02,120 --> 00:00:03,560
<font color="#FFFF00">Previously on Superman & Lois...</font>
2
00:00:03,560 --> 00:00:06,880
<font color="#00FFFF">I'm stepping down from active duty.</font>
<font color="#00FF00">You're going to be hard to replace.</font>
3
Etc., etc...
I'm rubbish at batch coding so I tried searching out possible ways to do it but nothing I tried worked!
Below are some attempts I made using different "routines" I found; each successive attempt separated (from last to first) by the PAUSE, EXIT commands:-
for %%a in (*.txt) do type append_ns0 >> %%a.srt
pause
exit
for %%a in (*.txt) do type append_ns0 >> %%a
for %%a in (*.txt) do type "%%~na" >> %%a
for %%a in (*.txt) do type append_spc >> %%a.srt
pause
exit
for %%I in (*.txt) do copy "C:\Users\wbcam\Desktop\G classroom\AddTitle.txt"+"%%~nI"+" "+"%%I" "%%~nI.srt"
pause
exit
for %X in (C:\Users\wbcam\Desktop\G classroom\Add Titles\*.txt) do type C:\Users\wbcam\Desktop\G classroom\AddTitles.txt >> %X
pause
exit
To use the COPY command I had to first rename the files from .srt to .txt (I'd rather NOT have to do that; I'm hoping someone can show me how to work on the ,srt files without any intermediate stages) and COPY also seemed to add a hex1A character to the end of the new file but, of course, it couldn't handle the insertion of the Filename (a text string) into the new file as it would only concatenate files not strings (if I, eventually, understood it's operation correctly, Doh!).
And attempts to use the ECHO or TYPE commands just seemed to overwrite everything in the original file leaving only:-
0
00:00:00,001 --> 00:00:02,100
and bugger all else!
Can anyone help out, please?
#ECHO OFF
SETLOCAL
rem The following setting for the source directoryis a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
FOR /f "delims=" %%b IN (
'dir /b /a-d "%sourcedir%\*.srt" '
) DO (
(
ECHO 0
ECHO 00:00:00,001 --^> 00:00:02,100
ECHO "%%~nb"
ECHO.
TYPE "%sourcedir%\%%b"
)>"%sourcedir%\%%~nb.txt"
MOVE "%sourcedir%\%%~nb.txt" "%sourcedir%\%%b" >NUL
)
GOTO :EOF
Always verify against a test directory before applying to real data.
Perform a directory scan. assigning each filename that matches the mask to %%b.
Write the two required lines, a line containing the name part of the filename in quotes and an empty line to the output, then type the contents of the selected file. Note that the > character in the text needs to be escaped by a caret. The output of the echoes is gathered and redirected to a .txt file and the .txt file is then written over the original file. The 1 file(s) copied message is suppressed by the >nul.
If you prefer, you could replace the two echo lines that insert the fixed text with type somefilename where somefilename contains the fixed text required.
You could replace the move line for testing with
FC "%sourcedir%\%%~nb.txt" "%sourcedir%\%%b"
which will show the differences.
copy adds the control-z following an archaic convention that ^Z marked end-of-file for text files.
--- Addition in response to comment
for /? from the prompt generates documentation about the for command - in general, commandname /? generates documentation for any command, although some commands require -? or -h or --h or --help. Utility designers generally follow the same convention and the details depend on the platform for which the software was originally designed.
for /f ... ('command....') do ... interprets the command-output as though it was a file.
The dir command reports the filenames and subdirectorynames in a directory. By default, it produces a formatted report including filesize and last-update date, but has numerous options including /b to produces a "basic" report (names only, no sizes, headers, summary, dates ,etc.) and /a-d which suppresses directorynames. The /s option means 'and list subdirectories too'. It can also accept a filemask (or ambiguous filename) - which applies equally to directorynames. If the filename supplied contains a ? then this means any ONE character or * which means any number of any characters - any other characters are taken literally and will match regardless of case. Hence, *.srt means any number of any characters followed by .srtwhich should select all file/directorynames that end.srt. The filemask may be preceded by *directoryname\\* which means scan this directory- but *that* directoryname may **not** contain?or*`.
When for /f is supplied with a filename list in basic form, each filename in the list is assigned to the metavariable assigned (%%a in this example) in turn and the do statements are executed using the assigned value in the metavariable. Again, there are many ways of having for /f interpret the value-string that is assigned to the metavariable. Many, many examples of these on SO
In the days of CP/M, filesize was a number of 128-byte blocks. Since files were not often a multiple of 128 bytes long, convention was that a control-z marked the end-of-file. Backwards-compatibility is a big issue in computing as it's wasteful to revise existing systems to cater for a concept-revision. Hence, the control-Z convention is still recognised and observed for text files.

Why do XCOPY /W and REPLACE /W consume all redirected text data for their one-character prompt?

When having a text file test.txt containing this1:
test
data
The following code returns the content of the text file with the first character removed:
< "test.txt" (
> nul pause
findstr "^"
)
The same happens when using a pipe:
type "test.txt" | (
> nul pause
findstr "^"
)
Because the pause command takes exactly one character.
However, when replacing the pause command by either of the following commands, the output is empty, although – like pause – they prompt (/W) for a single character only:
2> nul xcopy /W ? .
replace /W /U ? .
Why is this, what happens here?
Are xcopy /W and replace /W consuming all redirected/piped text data, even multiple lines, although they display only the first character they receive? Are they messing around with the file pointer?
Is there a way to prevent these commands from absorbing more than a single character?
1… With the last line terminated by a line-break in order for findstr not to hang indefinitely – see this thread: What are the undocumented features and limitations of the Windows FINDSTR command?, section »FINDSTR hangs on XP and Windows 7 if redirected input does not end with <LF>«.
The behavior actually varies a bit between redirection vs a pipe.
Redirection
It appears that XCOPY and REPLACE simply move the stdin file pointer to End-Of-File (EOF).
If you use FIND /V "" instead of FINDSTR "^", then you get the entire file as output because FIND resets the file pointer to the beginning of the file upon startup. So the stdin stream is still valid after XCOPY runs, but the file pointer is at EOF.
XCOPY must be moving the file pointer without actually reading all of the data because when I use big.txt containing 0.5 GBytes, it is still "instantaneous". There would be a significant delay if XCOPY had to read all of the file before it proceeded.
Pipes
I don't fully understand the implications of this, but I believe the standard pipe is block buffered. XCOPY and REPLACE read up to 1 full block before they continue, leaving the remainder of the piped data for FINDSTR to read.
I piped in a 127 line file containing 4191 bytes, and FINDSTR output 97 bytes over 3 lines. So in this case XCOPY appears to read 4094 bytes.
I then piped in a 5000 byte file without any new lines and FINDSTR output 908 bytes, meaning XCOPY appears to read 4092 bytes.
So the pipe block buffer must be somewhere in the neighborhood of 4 kbytes. My guess is XCOPY simply moves the file pointer to the end of the current buffer without reading all of the data, but I don't know how to test that.
As would be expected, substituting FIND for FINDSTR does not change the result when using pipes - there is no way to reset the file pointer back to the beginning of the piped data after it has already been read.
I can't imagine that there is any way to modify the behavior of XCOPY or REPLACE... it is what it is.
As for why XCOPY and REPLACE behave the way they do?... I haven't a clue. The Microsoft "batch" world is so idiosyncratic that I gave up asking why long ago.

Gather files of a similar filename programmatically

I have a photobooth that will dump all the pictures as they're taken onto a helper PC on the same network.
On the helper PC, I had planned to have a scheduled task run every x minutes to perform some edits on the images using ImageMagick (eg montage, label, etc).
The files are named via timestamp and it takes 4 at a time, so if a few people back to back used the photobooth, then it would transfer the following files to the helper PC:
2016-01-29-22-05-01.jpg
2016-01-29-22-05-02.jpg
2016-01-29-22-05-03.jpg
2016-01-29-22-05-04.jpg
2016-01-29-22-07-01.jpg (note the timestamp change)
2016-01-29-22-07-02.jpg
2016-01-29-22-07-03.jpg
2016-01-29-22-07-04.jpg
I was initially using a batch file to just serve *.jpg into ImageMagick, but now I realise if two people use the photobooth in < x minutes, then there will be two sets of images (like the above). Handing 8 pictures instead of 4 to ImageMagick will result in a mess.
My question is - how can I identify files which a similarly named (eg, first 16 characters are the same) and just pass these to ImageMagick?
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\transferred.log"
IF NOT EXIST "%filename1%" ECHO xxx>>"%filename1%"
:again
SET "transfer="
FOR /f "tokens=1-6delims=-" %%a IN (
'dir /b /a-d /on "%sourcedir%\*.jpg"^|findstr /b /v /g:"%filename1%"'
) DO (
SET "transfer=%%a-%%b-%%c-%%d-%%e"
GOTO movefiles
)
ECHO no new files to MOVE
GOTO :EOF
:movefiles
>>"%filename1%" ECHO %transfer%
:: don't know what you want from here
ECHO transfer %transfer%* ??
SET "transfer=%transfer%-01.jpg %transfer%-02.jpg %transfer%-03.jpg %transfer%-04.jpg"
ECHO transfer %transfer% ??
:: perhaps a delay here for helper PC to process??
timeout /t 5 >nul
GOTO again
You would need to change the settings of sourcedir and filename1 to suit your circumstances.
You haven't told us what happens to the files you want grouped, so I've assumed they remain.
The meat of the matter is the for - grab the first 6 tokens using - as separators and apply to a directory-list in basic form without directories sorted in name order and filtered for lines that begin with a string which does not appear in filename1
If any such line is found, gather the name into transfer, and do something with that value (not specified), recording the value in the log so that the next loop includes the value found in its exclude-list.
I don't have time to code this, but you could do the following
Get current date and time to the second
Format it like the filenames (set mydatetime=2016-01-29-22-07)
for /r %i in (%mydatetime%*) do move %i %targetpath%
wait 1 minute PING 127.0.0.1 -n 1 -w 60000 >NUL
loop
(this assumes only one batch per minute as you indicated in the comments above)
However, if you have the ability to modify the output (as indicated by adding "a couple of 1 second sleeps") I wonder like everyone else why you don't just make the output more robust? Separate folders, batch names, etc.

Sox --multi-thread option: Split stereo wav file to 2 mono files Windows Line command

I've written a batch program in windows command-line to take a stereo wav file and split the right and left channel into separate FLAC files using sox v.14.4.0 audio program. Like so..
for /r %%n in (*.wav) do (
C:\sox\sox.exe %%n -c 1 %%n.left.flac remix 1
C:\sox\sox.exe %%n -c 2 %%n.right.flac remix 2)
However, this takes a bit longer than I'd like. I looked into this more and it appears that I can use SOX_OPTS and the --multi-thread option along with --buffer in order to perform two sox operations at the same time.
The Sox documentation states this:
The SOX_OPTS environment variable can be used to provide alternative default values for SoX’s global options. For example:
SOX_OPTS="−−buffer 20000 −−play−rate−arg −hs −−temp /mnt/temp"
Note that the way to set environment variables varies from system to system.
Here are some examples: MS-DOS/MS-Windows:
set SOX_OPTS=−V −−no-clobber −−buffer BYTES, −−input−buffer BYTES
Set the size in bytes of the buffers used for processing audio (default 8192).
−−buffer applies to input, effects, and output processing;
−−input−buffer applies only to input processing (for which it overrides −−buffer if both are given).
Be aware that large values for −−buffer will cause SoX to be become slow to respond to requests to terminate or to skip the current input file.
Two questions.
Is it possible to do split 1 input file to 2 output files simultaneously using --multi-thread as explained above or another way?
If so, how? (note: I haven't been able to figure out the correct syntax to get SOX_OPTS from the documentation above to work on it's own never mind in a batch file)
Also: I've found some other stackoverflow questions similar to this but always using java, php, linux, etc. Unfortunately, I don't have any knowledge of those programs. Please be sympathetic of my programming ignorance.
No idea without getting more familiar than I'd prefer with your SoX...
However, I'd try this:
As a test, use a single .wav file
for /r %%n in (justone.wav) do (
START "Left" C:\sox\sox.exe %%n -c 1 %%n.left.flac remix 1
START "Right" C:\sox\sox.exe %%n -c 2 %%n.right.flac remix 2)
And perhaps it'll run two instances simultaneously on a multi-processor machine. (Window title is "Left" or "Right")
Now - if this works, then try
#ECHO OFF
SETLOCAL
for /r %%n in (*.wav) do CALL :runsox "%%n"
GOTO :eof
:runsox
CALL :wait
START "%~1.left" C:\sox\sox.exe %~1 -c 1 %~1.left.flac remix 1
CALL :wait
START "%~1.right" C:\sox\sox.exe %~1 -c 2 %~1.right.flac remix 2
GOTO :eof
:wait
FOR /f %%c IN ('tasklist^|find /i /c "sox.exe"') DO SET running=%%c
IF %running% GEQ 6 timeout /t 5 >nul&GOTO wait
GOTO :eof
What this will do is execute the :runsox procedure for each .wav encountered, passing the .wav` name.
The runsox procedure will call the wait procedure - which I'll come to. When the WAIT is finished, it'll put on the left sox, then wait again and put on the right, then return for the next filename until all's done. The %~1 in the names means 'the first parameter given to this routine, minus any enclosing quotes` - and that's the filename (%%n) provided by the CALL.
the wait procedure finds out how many SOX.EXE instances are running by passing a list of tasks from tasklist through a find filter to find the string sox.exe /i means regardless of case and /c means output a count.
Running therefore acquires a count of the instances of SOX in progress. If that is greater than or equal to 6 (an arbitrary number - choose it to suit yourself. Best to leave one or two processors for other tasks, so 6 id good for an 8-processor machine) then a timeout of 5 seconds is executed and after 5 seconds the tasklist is examined again.
When there are fewer than 6 instances, the wait procedure exits and the next task is started.
So - the WAIT procedure waits until there are fewer than 6 SOX instances.
Change the 6 to suit your machinery and the 5 your aesthetics.
Edit: remove legacy closing-parenthesis

How to avoid batch line limit (8191 chars per line)?

I have a multiple line file (about 300 - 400 lines) each line has 72 characters and i need that transformed into a single line.
Any ideas ?
This is possible, assuming you want your concatenated line in one line in a text file. However, even though you can create the long line with batch, you will not be able to read the line using batch. As Electro Hacker says, you cannot create a batch environment variable longer than 8191 bytes long.
XP SET /P will preserve leading spaces from each line. But SET /P on Vista and beyond strips leading spaces.
This solution adds a space between each concatenated line.
#echo off
setlocal
set "infile=test.txt"
set "outfile=out.txt"
>"%outfile%" (
for /f usebackq^ delims^=^ eol^= %%A in ("%infile%") do <nul set /p "=%%A "
)
If you want to stick to standard Windows tools, PowerShell would also be an option:
-join (Get-Content foo.txt)
You can't break a limitation of the OS, you can't break the 255 chars path in Windows, and you can't break the CMD interpreter lenght limitation, simply as that!
Sorry but you can't store that line into a var, no way, don't exist any magic, computers are logical.
But it's not the end of the world, you can do it so easy in any other lenguage, I recommend you Ruby or python (Ruby for that), it's an easy job, open a file, store the content into a var, and then do what you want, don't need any experience for that, if you need a example just comment this.

Resources