for /f*tokens usage - variable out of scope

Microsoft Windows
Post Reply
User avatar
MigrationUser
Posts: 336
Joined: 2021-Jul-12, 1:37 pm
Contact:

for /f*tokens usage - variable out of scope

Post by MigrationUser »

27 Apr 2012 23:58
Cozmo

I have a script that I am working on that used to be used for only one client. Now we're going to add a few more clients. My thought is that I could specify the tokens at the beginning of the script and then use them throughout the whole script until the end. I believe this is possible, but I'm not too sure.

for /f "tokens=1-5 delims=," %%a IN (%binDIR%\Info.txt) do

Info.txt consisting of (examples):
apple,salt,sugar,xy217,easy
orange,pepper,ranch,yw111,hard

So that throughout the script wherever I may need apple or orange I only need to specify %%a, easy or hard %%e and allow the script to decide what belongs to what. I understand this part, my concern is with ERRORLEVEL checking and with anything I'm not noticing that others can tell me. Has anybody ever done something like this before? Do you have any recommendations, please? Thank you in advance. :)

----------------------------

#2 28 Apr 2012 13:47
bluesxman


Don't forget you can only reference your FOR tokens from within the FOR loop, if you're planning to do stuff with them outside of that you'll need to assign variables.

I'm guessing you're going to want to put your values into a multidimensional array, in which case you could do something like this:

Code: Select all

@echo off

set count=0

for /f "usebackq tokens=1-5 delims=," %%a IN ("%binDIR%\Info.txt") do (
   call :array_add "%%~a" "%%~b" "%%~c" "%%~d" "%%~e"
)

REM the rest of your script goes here
REM you can get values out like so:

call :array_get 1 a
if not errorlevel 1 set yourVal

call :array_get 2 e
if not errorlevel 1 set yourVal

call :array_get 3 a
if not errorlevel 1 set yourVal

call :array_get 1 f
if not errorlevel 1 set yourVal

pause

goto :EOF

:array_add

REM create an array row

set /a count+=1

set "array_%count%_a=%~1"
set "array_%count%_b=%~2"
set "array_%count%_c=%~3"
set "array_%count%_d=%~4"
set "array_%count%_e=%~5"

goto :EOF

:array_get

REM get a chosen token from an array row

set "line=%~1"
set "token=%~2"
set "yourVal="

if not defined array_%line%_%token% (
	echo NOT DEFINED: Line "%line%" / Token "%token%"
	exit /b 1
)

REM this line is just to illustrate what's going on
call echo:Token "%token%" for line %line% = "%%array_%line%_%token%%%"

REM sets yourVal as the chosen item
call set "yourVal=%%array_%line%_%token%%%"

goto :EOF
Last edited by bluesxman (28 Apr 2012 14:00)

cmd | *sh | ruby | chef

----------------------------

#3 28 Apr 2012 14:47
dbenham
bluesxman wrote:

Don't forget you can only reference your FOR tokens from within the FOR loop, if you're planning to do stuff with them outside of that you'll need to assign variables.
Generally true, but it is possible to access a FOR variable that would seem to be out of scope! If you CALL out of a FOR loop, the variables are now out of scope and inaccessible. But if you instantiate another FOR within the subroutine, the parent FOR variables are now accessible.

Code: Select all

@echo off
for %%A in (XX) do call :sub
exit /b

:sub
echo %%A - FOR var in called subroutine is out of scope
for %%B in (dummy) do echo %%A - FOR var in called secondary FOR is in scope
exit /b
output

Code: Select all

%A - FOR var in called subroutine is out of scope
XX - FOR var in called secondary FOR is in scope
This "feature" can cause some pain if you are not aware of it. But it can also be exploited as a useful tool.

Dave Benham

----------------------------

#4 28 Apr 2012 17:44
bluesxman


Huh, new one on me -- thanks, will keep that in mind.

Every day's a school day, it seems.

cmd | *sh | ruby | chef

----------------------------

#5 29 Apr 2012 03:22
Cozmo


Thanks, guys! I think I'm actually more confused than before though. This is the process I'm trying to span it through:

Files are placed in a dir (d:batch\files\)

Besides setting up date/time variables and set variables this batch runs to check for their presence as files can be either apple_salt_YYYYMMDDHHMMSS.csv.pgp or orange_pepper_YYYYMMDDHHMMSS.csv.pgp files can and will arrive randomly.

Code: Select all

for /f "tokens=1-5 delims=," %%a IN (%binDIR%\Info.txt) do (
IF EXIST %files%\%%a_%%b_*.pgp FOR /f "delims=" %%f in ('dir /b %files%\%%a_%%b_*.pgp') do (
               gpg --decrypt -r "%%b" -r "password" –output %processed%)
Then some errorlevel checking in case there are errors decrypting, these including email notifications and the typical backup of files. Successfully decrypted files get moved to a different location we'll call %in%.
Next the batch will check an %out% dir for files that already have been processed to then encrypt and upload. This is why I was hoping I could have a reference at the very beginning of the batch to differentiate between the files that may be received and/or delivered (file naming doesn't change much at all). Or something that could bind the entries in the Info.txt file throughout the script. Initially, I really thought that "for /f" would do this nicely.

Info.txt consisting of (examples):
apple,salt,sugar,xy217,easy
orange,pepper,ranch,yw111,hard

Arrays might do the trick. Trying to understand it better though. Or maybe I am over-complicating and there's an easier way, perhaps? Please let me know. :)

----------------------------

#6 02 May 2012 19:29
Cozmo


I changed the way files process when the files come in. I'm not sure how to specify only the potentially bad file that doesn't decrypt is moved over to a different directory (as a bad file can be random), but it must continue processing the rest. While the process still runs and the good files still go where I want them to go all of the pgp files get placed in the error directory and I only want the bad one to go there not all.

DIR /o/b *.pgp > %batchDir%Files.txt
SetLocal EnableDelayedExpansion
Set n=
Set _InputFile=%batchDir%Files.txt
For /F "tokens=*" %%I IN (%_InputFile%) DO (
Set /a n+=1
Set _var!n!=%%I
)
gpg --multifile --passphrase-file %batchDir%passphrase.txt --decrypt %batchDir%*
ECHO.ERROR LEVEL: %ERRORLEVEL%
IF %ERRORLEVEL% NEQ 0 GOTO DECERROR

Any ideas or am I overlooking something obvious? I'm hoping I haven't stumped you guys :)

----------------------------

#7 04 May 2012 00:05
Cozmo


Nobody have any ideas?

----------------------------

#8 04 May 2012 12:34
dbenham


If you want to take action upon gpg error, then I think you will have to decrypt each file individually. I don't know the arguments of gpg, so I can only provide pseudo-code

Code: Select all

for /f "delims=" %%F in ('dir /o /b *.pgp') do (
  gpg {file %%F + other args} && (
    rem Success actions go here
  ) || (
    rem Failure actions go here
  )
)
Dave Benham

----------------------------

#9 04 May 2012 21:30
Cozmo


Thanks, Dave! I actually couldn't use your script exactly that way, though only because in my testing I was seeing that GPG wasn't passing an ERRORLEVEL (I thought it should!?!). In the end I still had to use a subroutine as such:

Code: Select all

SetLocal EnableDelayedExpansion
for /f "delims=" %%A in ('dir /o/b %files%*.pgp') do (
gpg --multifile --passphrase-file %binDir%.\gpg_keys\passphrase.txt --decrypt %%A
ECHO A is %%A
set x=%%A
set str=!x:.pgp=!
echo str is !str!
call :CHECKFILE !str!
echo result is !result!
IF [!result!]==[true] ECHO File %%A decrypted successfully!
IF [!result!]==[false] MOVE %%A %failed% && ECHO File %%A failed to decrypt
)
GOTO MV2

:CHECKFILE
IF EXIST %1 set RESULT=true
IF NOT EXIST %1 set RESULT=false
goto :eof
----------------------------

#10 05 May 2012 09:03
RG


Here's one way to simplify things a bit.

Code: Select all

for /f "delims=" %%A in ('dir /o/b %files%*.pgp') do (
   gpg --multifile --passphrase-file %binDir%.\gpg_keys\passphrase.txt --decrypt %%A
   call :CHECKFILE "%%A"
)
GOTO MV2

:CHECKFILE
IF EXIST "%~n1" echo.File %~1 decrypted successfully!
IF NOT EXIST "%~n1" echo.File %~1 failed to decrypt
goto :eof 
We could use an ELSE statement instead of 2 IF statements in :CHECKFILE, but I chose the method you did because otherwise you need to quote the %~n for the echo in case there are parens in the filename.

Last edited by RG (05 May 2012 09:08)

Windows Shell Scripting and InstallShield

----------------------------

#11 06 May 2012 05:42
Cozmo


Thanks, RG! That does make it much cleaner. Thank you :)

----------------------------

#12 06 May 2012 14:04
dbenham


I suppose it doesn't do any harm, but why the --multifile option? You are decrypting one file at a time, so it seems unnecessary and obfuscating.

----------------------------

#13 08 May 2012 08:54
Cozmo


Removing the --multifile option causes other strange issues, most notably the piping of the internal file data into the log file. Leaving --multifile in as part of the script is causing no harm so I decided to leave it in there for now.

Actually, GPG does state the following regarding the --multifile option: This modifies certain other commands to accept multiple files for processing on the command line or read from STDIN with each filename on a separate line. This allows for many files to be processed at once.
Post Reply