Shell script hash table - shell

I'm trying to translate a .bat file into a .sh script. Several parameters are passed to the script, one of these being a hash table. The code looks like...
date /T
time /T
FOR /F "tokens=1-11" %%A IN (%4) DO (
set args1=%%A %%B %%C %%D %%E %%F %%G %%H %%I %%J %%K
)
FOR /F "tokens=12" %%A IN ("%4") DO (
set args2=%%A
)
FOR /F "tokens=12*" %%A IN (%4) DO (
set dummy=%%A
set args3=%%B
)
I'm not sure what is going on here, or how to handle it?
Any suggestions? Or good reference pages online I can take at look at?

Here is a good reference page: http://technet.microsoft.com/en-us/library/bb490909.aspx
Breakdown
The first loop is treating the input as a filenameset.
This is storing the first 11 whitespace delimited items in the variable args1.
The second loop is treating the input as a literal string.
This is storing just the 12 whitespace delimited item in the variable args2.
The last loop is treating the input as a filenameset.
This is storing all the remaining whitespace delimited items after the 12th item in the variable args3.
Example
I would recommend adding the echo command after each loop so you can see what the parsed values look like.
FOR /F "tokens=1-11" %%A IN (%4) DO (
set args1=%%A %%B %%C %%D %%E %%F %%G %%H %%I %%J %%K
)
echo %args1%
FOR /F "tokens=12" %%A IN ("%4") DO (
set args2=%%A
)
echo %args2%
FOR /F "tokens=12,*" %%A IN (%4) DO (
set args3=%%B
)
echo %args3%

Related

Windows Batch File: Loop on rows and split string

I have a text file with the following format:
name1:surname1
name2:surname2
name3:surname3
and so on.
I need to write a for loop in window batch script and assign to 2 variables
name=name1
surname=surname1
and so on. Something like (this is wrong)
for /F "tokens=*" %%row in (myfile.txt) do (
for /F "tokens=1* delims=:" %%u in ("%row%") do (
....
)
)
Any suggest?
You don't really need two nested loops for that.
What you probably will need, however, is delayed variable expansion.
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1,2 delims=:" %%u in (myfile.txt) do (
set "name=%%u"
set "surname=%%v"
echo !surname!, !name!
)
outputs this for me:
surname1, name1
surname2, name2
surname3, name3
Delayed variable expansion is what allows you to assign the single-letter loop variables (u and v in this case) to real variables and use them in the rest of the loop, by accessing them with ! instead of %.
for /F "tokens=*" %%r in (myfile.txt) do (
for /F "tokens=1* delims=:" %%u in ("%%r") do (
....
)
)
or
for /F "tokens=1* delims=:" %%u in (myfile.txt) do (
....
)
Metavariables live r and u are limited to a single (case-sensitive) alphabetical character.
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=1,2 delims=:" %%u in (myfile.txt) do (
set name=%%u
set surname=%%v
echo name=!name!
echo surname=!surname!
)
output:
name=name1
surname=surname1
name=name2
surname=surname2
name=name3
surname=surname3

Reading list of files in a directory and copying the contents using batch command file

I have a list of csv files in a directory which have name with format XX_YYYFile.csv, where XX is a name that can have any characters (including space), and YYY is random 3 digits. For example: "book_123File.csv", "best movie_234File.csv", etc. I want to read this list of files then create new CSV files by removing "_YYYFile". The content of the new files are the same with the original ones, except the first line needs to be added with value "number,name,date".
set inputFileFolder=C:\Input
set outputFileFolder=C:\Output
FOR /F "delims=" %%F IN ('DIR %inputFileFolder%\*File.csv /B /O:D') DO (
set reportInputFile=%inputFileFolder%\%%F
set reportInputFileName=%%F
set result=!reportInputFileName:~0,-12!
set reportOutputFileName=!result!.csv
set reportOutputFile=%outputFileFolder%\!result!.csv
echo number,name,date > !reportOutputFile!
for /f "tokens=* delims=" %%a in (!reportInputFile!) do (
echo %%a >> !reportOutputFile!
)
)
If I run this batch file, file "book.csv" is successfully created with the correct contents (first line: "number,name,date", the next lines are from file "book_123.csv"). But file "best movie_234.csv" and other files contain space in the filename are not created successfully. File "best movie.csv" is created with only 1 line "number,name,date". The contents of file "best movie_234.csv" are not copied to file "best movie.csv".
Please help.
You need to Escape Characters, Delimiters and Quotes properly. Note the usebackq parameter in inner for /F loop as well:
#ECHO OFF
SETLOCAL EnableExtensions EnableDelayedExpansion
set "inputFileFolder=C:\Input"
set "outputFileFolder=C:\Output"
FOR /F "delims=" %%F IN ('DIR "%inputFileFolder%\*File.csv" /B /O:D') DO (
set "reportInputFile=%inputFileFolder%\%%F"
set "reportInputFileName=%%F"
set "result=!reportInputFileName:~0,-12!"
set "reportOutputFileName=!result!.csv"
set "reportOutputFile=%outputFileFolder%\!result!.csv"
>"!reportOutputFile!" echo number,name,date
for /f "usebackq tokens=* delims=" %%a in ("!reportInputFile!") do (
>>"!reportOutputFile!" echo %%a
)
rem above `for /f ... %%a ...` loop might be replaced by FINDSTR
rem >>"!reportOutputFile!" findstr "^" "!reportInputFile!"
rem or by TYPE
rem >>"!reportOutputFile!" type "!reportInputFile!"
)
Hint: each > and >> redirector works as follows:
opens specified oputput file, then
writes something to oputput file, and finally
closes oputput file.
This procedure might be extremely slow if repeated in next for /f ... %%a ... loop for larger files:
>"!reportOutputFile!" echo number,name,date
for /f "usebackq tokens=* delims=" %%a in ("!reportInputFile!") do (
>>"!reportOutputFile!" echo %%a
)
Use block syntax rather:
>"!reportOutputFile!" (
echo number,name,date
for /f "usebackq tokens=* delims=" %%a in ("!reportInputFile!") do (
echo %%a
)
)
above for /f ... %%a ... loop might be replaced by FINDSTR command (it eliminates empty lines like for does) as follows:
>"!reportOutputFile!" (
echo number,name,date
findstr "^." "!reportInputFile!"
)
or by TYPE command (it will retain empty lines unlike for) as follows:
>"!reportOutputFile!" (
echo number,name,date
type "!reportInputFile!"
)

How to read and find the largest value from .csv with the use of window batch

In my .csv file,
abc,10/24/2013,ABC
cede,5/1/2013,ABCk
cdeh,7/27/2014,ABCf
cdedsf,1/27/2014,gfABC
.
.
.(1xx more lines with similar text)
I would like to find the latest date in the middle field (e.g. 7/27/2014 in above case) and save to a variable named as "latest_date".
However, I do not know how to read the specific field from a .csv file and find the latest date with such format(M/D/Y).
Can anyone teach me?
This simpler method should run faster:
#echo off
setlocal EnableDelayedExpansion
set latest_num=0
for /F "tokens=2-4 delims=,/" %%a in (theFile.csv) do (
set /A "new_num=((%%c*100)+%%a)*100+%%b"
if !new_num! gtr !latest_num! (
set latest_num=!new_num!
set latest_date=%%a/%%b/%%c
)
)
echo Latest date: %latest_date%
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "tokens=1-3 delims=/ " %%a in (
' cmd /q /v:on /c "for /f "tokens^=2-4 delims^=^,/" %%a in (data.csv) do (set /a "x^=%%c*10000+%%a*100+%%b" >nul & echo(^!x:~0,4^!/^!x:~4,2^!/^!x:~-2^!)" ^| sort /r '
) do set "last_date=%%b/%%c/%%a" & goto done
:done
echo %last_date%
How does it work?
The input file is readed via a for /f loop (the inner one). Each record is tokenized, using the commas and slashes as delimiters. This leaves the tokens 2 to 4 as the elements of the date. This elements are normalized (month and days have one or two digits, years are at the end) with some arithmetics to get a yyyy/mm/dd date, and the resulting dates are echoed. This list of dates is sorted in inversed order (so the greatest date is in the first record).
As the process in defined as a pipe (each process inside a pipe run in a separate cmd instance), and as the left part of the pipe requires delayed expansion enabled, the for /f that reads the file is executed inside its own instance of cmd with the adecuated configuration: echo off (/q) and delayed expansion active (/v:on).
The sorted list, will be readed with another for /f loop (the outer one), that will tokenize the retrieved data, separating again the year, month and day, so the final variable have the required format (mm/dd/yyyy). As the greatest date is in the first record, once it is retrieved and the value assigned to the variable, a goto jump to a label is executed to skip the rest of the records.
To see it clear, this is the same code, more readable, but separated in steps and using a temporary file
#echo off
setlocal enableextensions enabledelayedexpansion
set "tempFile=%temp%\%~nx0.%random%.tmp"
( for /f "tokens=2-4 delims=,/" %%a in (data.csv) do (
set /a "x=%%c*10000+%%a*100+%%b"
echo(!x:~0,4!/!x:~4,2!/!x:~-2!
)
)> "%tempFile%"
for /f "tokens=1-3 delims=/ " %%a in (
' type "%tempFile%" ^| sort /r '
) do set "last_date=%%b/%%c/%%a" & goto done
:done
echo %last_date%
del /f /q "%tempFile%" >nul 2>nul
endlocal
The inner loop in original code is now the first loop. File is readed by the for, date elements extracted (see tokens and delims), date normalized (set /a arithmetics) and the list saved to a temporary file
The outter loop in original code is the second one here. The file is readed with a type command, the data piped to sort /r and the resulting lines are tokenized by the for command to reformat the date.
Edited to adapt to comments
Aacini is right, his code is faster, but given that i just started this way, .... Anyway, the changes in this code can be translated to his solution
This should handle differences in date fields (aditional spaces and aditional initial 0) and missing fields.
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "tokens=2 delims=:" %%a in (
'cmd /q /v:on /c "for /f "delims^=" %%z in (data.csv) do for /f "tokens^=1-3 delims^=^," %%w in (" %%~z ") do if not "%%~y" equ "" for /f "tokens^=1-3 delims^=/ " %%a in ("%%~x") do (set /a "x^=%%c*10000 + 100%%a %% 100*100 + 100%%b %% 100" >nul & echo(^!x^!:%%~x)" ^| sort /r '
) do set "latest_date=%%a" & goto done
:done
echo %latest_date%
The inner for loops :
for %%z will read lines from file
for %%w will tokenize the readed line with an aditional space at the start and end of the line to prevent problems with adjacent delimiters removal.
for %%a handles the date normalization and outputs the calculated value used for sort and the readed date.
The outer for %%a loop will split the retrieved record to separate the calculated value from the readed date.
In the CMD the variable are displayes in alphabetical order and from the smaller to the bigger value.
So we transform all your dates in variables like this : set #142707=7/27/2014
Then looping against a set # we take the last value who is the latest date.
#echo off
setlocal enabledelayedexpansion
for /f "tokens=2 delims=," %%a in (data.csv) do call:checkDate %%a
for /f "tokens=2 delims==" %%a in ('set #') do set $LatestDate=%%a
echo The Latest Date : [!$LatestDate!]
exit/b
:CheckDate
set $out=
set "$Date=%1"
set "$Date=%$Date:/= %
for %%b in (%$Date%) do (
set $val=0%%b
set $out=!$val:~-2!!$out!)
set #!$out!=%1

how to add space in for /f "tokens=*"

this is my myfile.txt I want to add space in second column as see the sample
ARK,LAR SNE,QNE,898,ILO,SNE,SNE,LAR,LAR,545
AUS,MNY P08,TTL,7776,STO,STL,STL,MNY,MNY,567
BOS,MTZ TNK,SDK,444,PPO,TNK,TNK,MTZ,MTZ,456
this is the code I am using
for /f "tokens=* " %%i in (myfile.txt) do call :echo2 %%i %%J %%K %%L %%M %%N %%O %%P %%Q %%R %%S
goto :EOF
:echo2
echo insert ('%1','%2','%3','%4','%5','%6','%7','%8','%9','%10'); >>myfile1.txt
goto :EOF
its displaying results , where it should have taken space what I am missing any help is appreciated
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
(
FOR /f "delims=" %%i IN (myfile.txt) DO (
SET "dataline=%%i"
SET "outline="
CALL :adddata
)
)>myfile1.txt
GOTO :EOF
:adddata
FOR /f "tokens=1*delims=," %%p IN ("%dataline%"
) DO SET outline=%outline%'%%p',&SET "dataline=%%q"
IF DEFINED dataline GOTO adddata
ECHO insert (%outline:~0,-1%);
GOTO :eof
This should do the job with no practical limit on columns - provided of course that the comma is reliably an end-of-column delimiter.
For each line in the source file, assign the entire line to
dataline and clear outline
then take the first token, delimited by comma, from dataline, quote it,add a comma and append it to outline; then set dataline to the remainder of the line after the first comma.
repeat until there is nothing left in dataline
output the text insert ( + all but the last character of outline (which will be a comma) + );
If I understand you correctly, you want to preserve the spaces in the text between the 1st and 2nd comma, correct? Try this:
#echo off
for /f "tokens=1-10 delims=," %%a in (myfile.txt) do (
>>myfile1.txt echo.insert ('%%a','%%b','%%c','%%d','%%e','%%f','%%g','%%h','%%i','%%j'^);
)
Try this:
#echo off & setlocal
(for /f "delims=" %%i in (myfile.txt) do (
set "line='%%i'"
setlocal enabledelayedexpansion
set "line=!line:,=','!"
set "line=!line: = ','!"
echo(insert (!line!^);
endlocal
))>myfile1.txt
You can't exceed 9 variables so your script won't work after the 9th. You can use for /f to copy each line exactly as the original file like so:
for /f "tokens=* " %%i in (myfile.txt) do echo %%i >>myfile1.txt
goto :EOF

Parsing string in batch file

I have the following string:
MyProject/Architecture=32bit,BuildType=Debug,OS=winpc
I would like to be able to grab the values 32bit, Debug, and winpc and store them in variables named Architecture, BuildType, and OS to reference later in the batch script. I'm normally a Unix guy so this is new territory for me. Any help would be greatly appreciated!
This should do it:
FOR /F "tokens=1-6 delims==," %%I IN ("MyProject/Architecture=32bit,BuildType=Debug,OS=winpc") DO (
ECHO I %%I, J %%J, K %%K, L %%L, M %%M, N %%N
)
REM output is: I MyProject/Architecture, J 32bit, K BuildType, L Debug, M OS, N winpc
The batch FOR loop is a pretty interesting piece of machinery. Type FOR /? in a console for a description of some of the crazy stuff it can do.
Here is an interesting solution that doesn't care how many or what order the name=value pairs are specified. The trick is to replace each comma with a linefeed character so that FOR /F will iterate each name=value pair. This should work as long as there is only one / in the string.
#echo off
setlocal enableDelayedExpansion
set "str=MyProject/Architecture=32bit,BuildType=Debug,OS=winpc"
::Eliminate the leading project info
set "str=%str:*/=%"
::Define a variable containing a LineFeed character
set LF=^
::The above 2 empty lines are critical - do not remove
::Parse and set the values
for %%A in ("!LF!") do (
for /f "eol== tokens=1* delims==" %%B in ("!str:,=%%~A!") do set "%%B=%%C"
)
::Display the values
echo Architecture=%Architecture%
echo BuildType=%BuildType%
echo OS=%OS%
With a bit more code it can selectively parse out only name=value pairs that we are interested in. It also initializes the variables to undefined in case the variable is missing from the string.
#echo off
setlocal enableDelayedExpansion
set "str=MyProject/Architecture=32bit,BuildType=Debug,OS=winpc"
::Eliminate the leading project info
set "str=%str:*/=%"
::Define a variable containing a LineFeed character
set LF=^
::The above 2 empty lines are critical - do not remove
::Define the variables we are interested in
set "vars= Architecture BuildType OS "
::Clear any existing values
for %%A in (%vars%) do set "%%A="
::Parse and conditionally set the values
for %%A in ("!LF!") do (
for /f "eol== tokens=1* delims==" %%B in ("!str:,=%%~A!") do (
if !vars: %%B ! neq !vars! set "%%B=%%C"
)
)
::Display the values
for %%A in (%vars%) do echo %%A=!%%A!
Try the following:
#ECHO OFF
SET Var=MyProject/Architecture=32bit,BuildType=Debug,OS=winpc
FOR /F "tokens=1,2,3 delims=," %%A IN ("%Var%") DO (
FOR /F "tokens=1,2 delims==" %%D IN ("%%A") DO (
SET Architecture=%%E
)
FOR /F "tokens=1,2 delims==" %%D IN ("%%B") DO (
SET BuildType=%%E
)
FOR /F "tokens=1,2 delims==" %%D IN ("%%C") DO (
SET OS=%%E
)
)
ECHO %Architecture%
ECHO %BuildType%
ECHO %OS%
PAUSE

Resources