Explain batch command please - command-line

I want to create a folder with the name of the current date and time.
After searching a lot i found this which actually works.
Can someone explain what these batch commands do?
set timestamp=%DATE:/=-%#%TIME::=-%
set timestamp=%timestamp: =%
mkdir "%timestamp%"

Insert echo statements between the lines and you can see what the value of timestamp is
set timestamp=%DATE:/=-%#%TIME::=-%
echo %timestamp%
set timestamp=%timestamp: =%
echo %timestamp%
mkdir "%timestamp%"
Basically, the code is just removing the forward slash from the date and the colon from time since those are not valid directory names replacing them with hypens.
Read set /? Environment variable substitution to get a better idea.

set timestamp=%DATE:/=-%#%TIME::=-%
That's a string replacement.
1st:
%DATE:/=-% Replaces "/" character to "-" character in the DATE variable
(See: Echo %DATE% on your console)
2nd:
Adds the "#" character to the string after the DATE var and before the TIME var.
3rd:
%TIME::=-% Replaces ":" character to "-" character.
(See: Echo %Time% on your console)
set timestamp=%timestamp: =%
Next in that replacement replaces itself spaces to any characarter (so deletes spaces), but really any space is given so is not necessary in your example code.
You can learn more about Variable string replacement here: http://ss64.com/nt/syntax-replace.html
Also you can simplify your code 'cause no need to setting the value first:
mkdir "%DATE:/=-%#%TIME::=-%"

Related

Renaming multiple files with multiple field separator in awk

I need to rename following file likewise from PFSI4C.CSC.CCC.FSIContractData20211008.zip to TFSI4C.CSC.CCC.FSIContractData20211104.zip.
Every file's name should start with "T" and end up with current system date + .zip"
I am trying to loop over files and it looks like this:
for FILENAME in PFSI4C.CSC.CCC.FSIContractData20211008; do
NEW_FILENAME_HEADER=`echo $FILENAME | awk -F "." '{ print $1"."$2"."$3 }'` # which would takes PFSI4C.CSC.CCC.
NEW_FILENAME_SUFFIX=`echo $FILENAME | awk -F "[.|Data20]" '{ print "."$4 }'` # this part where I can't figure out to take only "FSIContract"
NEW_FILENAME="${NEW_FILENAME_HEADER}.""${NEW_FILENAME_SUFFIX}""Data20""${DATE}".zip" # which should make "TFSI4C.CSC.CCC.FSIContractData20211104.zip."
mv $FILENAME $NEW_FILENAME
done
FYI $DATE in our script defined like this: DATE='date +'%y%m%d' for example 211104
Thanks in advance!
With Perl's rename command you could try following code. I am using -n option here to test it in DRY RUN mode it will only print the file names from which file name(current) to which file name(required one) will be changed; remove -n in code once you are satisfied with shown output. Also DATE variable (DATE='20211104') is a shell variable which contains value of date needed to be in new file name.
rename -n 's:^.(.*)\d{8}(\.zip)$:T$1$2:; s:\.zip$:'"$DATE"'.zip:' *.zip
Output will be as follows:
rename(PFSI4C.CSC.CCC.FSIContractData20211008.zip, TFSI4C.CSC.CCC.FSIContractData20211104.zip)
Explanation of rename code:
-n: To run rename command in DRY RUN mode.
s:^.(.*)\d{8}(\.zip)$:T$1$2:;: Running 1st set of substitution in rename code. Where it creates 2 capturing group, 1st capturing group has everything from 2nd character onwards to just before 8 digits AND 2nd capturing group contains .zip at the end of filename. While substitution substituting it with T1$2 as per requirement.
s:\.zip$:'"$DATE"'.zip:: Running 2nd set of substitution in rename code. Where .zip$ with shell variable DATE along with .zip as per requirement.
First of all you should get the current date with date +%Y%m%d (4-digits year) instead of date +%y%m%d (2-digits year). The following assumes you do that. Prepend 20 to $DATE if it is not an option.
If your file names all look as the example you show bash substitutions can do it. First compute the length, extract the characters from second to last before date, prepend T, append $DATE.zip:
len="${#FILENAME}"
NEW_FILENAME="T${FILENAME:1:$((len-13))}$DATE.zip"
But you could also use sed, it offers a bit more flexibility. For instance it can deal with ending dates on a variable number of digits:
NEW_FILENAME=$(echo "$FILENAME" | sed 's/.\(.*[^0-9]\)\?[0-9]*\.zip/T\1'"$DATE"'.zip/')
Or, a bit more elegant with bash (here strings) and GNU sed or another sed that supports the -E option (for extended regular expressions):
NEW_FILENAME=$(sed -E 's/.(.*[^0-9])?[0-9]*\.zip/T\1'"$DATE"'.zip/' <<< "$FILENAME")
Assumptions:
replace first character (P in OP's sample) with T
replace last 10 characters (YYMMDD.zip) with $DATE.zip (OP has already defined $DATE)
all files contain 20YYMMDD so we don't need to worry about names containing strings like 19YYMMDD and 21YYMMDD
One idea using parameter substitutions (which also eliminates the overhead of subprocess calls to execute various echo, awk and sed commands):
DATE='211104'
FILENAME='PFSI4C.CSC.CCC.FSIContractData20211008.zip'
NEWFILENAME="T${FILENAME/?}" # prepend "T"; "/?" => remove first character
NEWFILENAME="${NEWFILENAME/??????.zip}${DATE}.zip" # remove string "??????.zip"; append "${DATE}.zip"
echo mv "${FILENAME}" "${NEWFILENAME}"
This generates:
mv PFSI4C.CSC.CCC.FSIContractData20211008.zip TFSI4C.CSC.CCC.FSIContractData20211104.zip
Once OP is satisified with the accuracy of the code the echo can be removed to enable execution of the mv command.

Using echo without trailing space in DOS

I noticed that when I use echo to print something to a file in DOS, a space is appended to the string. I need to print the string without the trailing space. Is there a way to do that, or as a workaround, remove trailing spaces from the file?
If I understood the problem correctly, you wrote the trailing space.
Instead of
echo string > file
use
echo string>file
Assuming you're talking about cmd.exe rather than the actual (rather outdated) MSDOS, there are a number of ways to do this, the first being:
echo Here is some text>output.txt
but I find that somewhat less than readable since I'm used to being able to clearly delineate 'arguments' on the command line.
Alternatively, there's nothing stopping you from swapping around the order of your command line:
>output.txt echo Here is some text
which will allow you to still separate the arguments whilst not having extraneous spaces put in your output file.
In fact, I've often used this method for blocks of code as well:
>output.txt (
echo hello
echo goodbye
)
which will write both lines to the file. I find it preferable in that case since you know right at the start where the output is going, rather than having to go and look at the end of the code block.
Some quick searching brought me this link
It seems you have multiple options. If you want to parse the file post-echo using a script, you could consider this VBScript
Do While Not WScript.StdIn.AtEndOfStream
WScript.Echo RTrim(WScript.StdIn.ReadLine)
Loop
which loops, line by line through a file(usually .txt), and performs RTrim, which strips trailing spaces.
Cheers

checking lines in a text files agaist another text file

I have two textfiles, how can i compare them?? Basically what i am after is somthing that take the first line from text file 1 and compares it against all the lines in text file 2, if that line does not appear write that line to text file 3.
Then check the next line in text file 1 agaist all the lines in text file 2 and so on.
The problem is trivial if you have a copy of grep for Windows. One good free source is GnuWin. You can download individual utilities like grep from the packages link, or you can get the entire GnuWin suite using the Download all link (click on the download button at the beginning of that page).
grep -v -x -F -f file2.txt file1.txt >file3.txt
-v = Inverts the match logic - lists lines that don't match
-x = The entire line must match exactly
-F = The search strings are string literals instead of regular expressions
-f file1.txt = get the search strings from file1.txt
You can almost do the same thing using the native FINDSTR command, except there are 2 problems:
1) Any backslash character \ in a search string must be escaped as \\, even when specifying a literal search.
2) There is a nasty FINDSTR bug that causes some matches to be missed if multiple case sensitive literal search strings are used.
See What are the undocumented features and limitations of the Windows FINDSTR command? for a "complete" list of undocumented FINDSTR issues.
The following will work as long as it is acceptable to do a case insensitive search and file2 does not contain any \ characters:
findstr /x /v /i /l /g:file2.txt file1.txt >file3.txt
The backslash limitation can be eliminated by creating a temp file that escapes the backslashes. It is a bit of code, but the end result still runs fairly quickly. The search must still be case insensitive.
#echo off
setlocal disableDelayedExpansion
::Define the files
set "file1=test1.txt"
set "file2=test2.txt"
set "file3=test3.txt"
::Create an LF variable containing a line feed character
set LF=^
::The above 2 blank lines are critical - do not remove
::Create a modified version of file2 that escapes any backslash
::EOL is set to a linefeed so that all non blank lines are preserved
::Delayed expansion is toggled on and off to protect ! characters
>"%file2%.mod" (
for /f usebackq^ eol^=^%LF%%LF%^ delims^= %%A in ("%file2%") do (
set "ln=%%A"
setlocal enableDelayedExpansion
echo(!ln:\=\\!
endlocal
)
)
::Find lines in file1 that are missing from file2.mod
findstr /vixlg:"%file2%.mod" "%file1%" >"%file3%"
::Delete the temporary file2.mod
del "%file2%.mod"
It is relatively simple to write a robust native batch solution using 2 FOR loops, but the performance will deteriorate quickly if the files are large.
#echo off
setlocal disableDelayedExpansion
::Define the files
set "file1=test2.txt"
set "file2=test.txt"
set "file3=test3.txt"
::Create an LF variable containing a line feed character
set LF=^
::The above 2 blank lines are critical - do not remove
::Find lines in file1 that are missing from file2.mod
::EOL is set to a linefeed character so that all non blank lines are preserved
>"%file3%" (
for /f usebackq^ eol^=^%LF%%LF%^ delims^= %%A in ("%file1%") do (
set "found="
for /f usebackq^ eol^=^%LF%%LF%^ delims^= %%B in ("%file2%") do (
if %%A==%%B set found=1
)
if not defined found echo %%A
)
)
There may be a simple and efficient native PowerShell solution, but that is not my expertise.

Unable to echo specific line to a .vbs file

The command prompt complains that it is unable to recoqnize the command.So i was thinking i needed to escape something that looked like a start of a command to the echo with the ^ character.
This is the exact line:
echo Set link = Shell.CreateShortcut(DesktopPath & "\Beta.lnk")>>%temp%\CreateFirefoxBetaShortcut.vbs
I tried:
echo ^Set link = Shell.CreateShortcut(DesktopPath & "\Beta.lnk")>>%temp%\CreateFirefoxBetaShortcut.vbs
But no luck.Im puzzled by this because it correctly enters much more complex lines but for some reason it want's to treat this line as a command not a simple text.
I can post the full .vbs including the other lines if that helps somehow.
The ampersand ("&") character has a special meaning for "cmd". Therefore, it must be preceded with a caret ("^"), like this:
echo Set link = Shell.CreateShortcut(DesktopPath ^& "\Beta.lnk")>>%temp%\CreateFirefoxBetaShortcut.vbs

nmake - how to force echo command to output the tab character?

How to force echo command to output a tab character in MS nmake makefile?
Verbatim tabs inserted right into a string after echo command are removed by nmake and don't show up in the output file.
all :
#echo I WANT TO OUTPUT THE <TAB> CHARACTER HERE! > output.txt
You can use a variable with TAB char. Put this lines in your .bat file:
set tab=[stroke TAB char from your keyboard]
echo a%tab%b>yourfile.txt
Doing so, yourfile.txt will have text a[TAB]b
As a workaround, you can create a file containing the tab character, named input.txt (not using nmake), and then say:
all :
#copy /b input.txt output.txt
I assume you already have tried to put the tab inside quotes?
all:
#echo "<TAB>" > output.txt
DOS and Windows have ugly text support in native batch files :).
Here is nice way to do your task:
install Python interpretator
write simple script which appends character with specified code to file
call script wherever you want :)
Simple script:
'''
append_char.py - appends character with specified code to end of file
Usage: append_char.py filename charcode
'''
import os
import sys
filename = sys.argv[1]
assert os.path.exists(filename)
charcode = int(sys.argv[2])
assert 0 <= charcode <= 255
fh = open(filename, 'ab')
fh.seek(0, os.SEEK_END)
fh.write(chr(charcode))
fh.close()
using this script from batch file you can create any possible file in universe :)
output.txt:
<<output.txt
I WANT TO OUTPUT THE <TAB> CHARACTER HERE!
<<KEEP
<TAB> represents a literal tab character here of course.
I had the same need. I used the answer using the quotes around the character and just took it one step further.
{tab} means pressing the keyboard tab key in the text editor.
SET tab="{tab}"
SET tab=%tab:~1,1%
The second line extracts the middle character from the quoted string.
Now you can use the %tab% variable with ECHO and, I suspect, anywhere else it's needed.
ECHO %tab%This text is indented, preceded by a tab.
I suspect this technique could be used with other problem characters as well.