I am using CSVDE to export some user info out of AD. I need to loop over several domain controllers and export data from each one to a separate csv file. To do so, I am using the following batch script (only 1 domain controller / file is displayed for simplicity):
ECHO %time%
#ECHO OFF
SET userName=SomeLogin
SET pass=SomePassword
SET domain=SomeDomain
SET columnName=-l givenname,lastLogon,userAccountControl,lastLogonTimestamp,sn,userPrincipalName,displayname,mail,samaccountname,pwdLastSet,whenCreated,whenChanged SET filter="(&(objectClass=User)(objectCategory=Person))"
SET len=1
SET data[0].file=some_file.csv
SET data[0].DC=someIP
SET i=0
:LOOP
IF %i% equ %len% GOTO :EOF
SET cur.file=
SET cur.DC=
FOR /f "usebackq delims==. tokens=1" %%j IN (`SET data[%i%]`) DO (
SET cur.%%k=%%l
)
csvde -f %cur.file% -r %filter% %columnName% -s %cur.DC% -b %userName% %domain% %pass% -u
SET /a i = %i% + 1
#ECHO ON
ECHO %time%
When I run this script, I get the following error:
Invalid Parameter: Bad argument
'(&(objectClass=User)(objectCategory=Person))'
When I run CSVDE outside of this script (i.e., outside of a loop), it runs just fine. So, it must have something to do with the fact that it's in a loop. But, I can't figure out why and how to resolve it. What do I need to change to resolve this issue?
Related
What I'm trying to make is a simple script to choose a network drive to be mounted then unmounted from a list. The information is storred in a CSV file.
The script first reads the CSV file and stores the parameters in an array of objects.
When I try to use the parameters in a net use command, I get a System Error 67.
Here is my code :
#echo off
setlocal EnableDelayedExpansion
set i=0
for /f "usebackq skip=1 tokens=1-4 delims=;" %%a in ("data.csv") do (
set list[!i!].name=%%a
set list[!i!].endpoint=%%b
set list[!i!].user=%%c
set list[!i!].pwd=%%d
set /a i=!i!+1
)
set "x = 0"
:SymLoop
if defined list[%x%] (
call echo %list[%x%]%%
set /a "x+=1"
goto :SymLoop
)
set /a "x+=1"
for /l %%i in (0 1 %x%) do (
call echo %%i %%list[%%i].name%%
)
set /p tomount=Which network drive would you like to mount ?
net use z: %%list[%tomount%].endpoint%% /user:%%list[%tomount%].user%% %%list[%tomount%].pwd%%
set /p dummy=Hit ENTER to unmount and exit...
net use z: /delete
pause
The CSV file looks like this (these are examples, I use correct data in my scenario):
Nom;Endpoint;ID;Password
test1;\\1.1.1.1\dir1\dir2;admin;pssword
test2;\\2.2.2.2\dir1\dir444;admin;testpwd
When I call echo %%list[%tomount%].endpoint%% /user:%%list[%tomount%].user%% %%list[%tomount%].pwd%%, I get the correct variables, and when I manually enter the net use command with the exact same parameters, it works too.
Do you have any idea how to solve this ?
Thanks.
I simply forgot to use call before my net use command !
Works now.
I write two for loops to do data automation. While variables echoed well in each loop, the last step (data process using a well-written batch) keeps giving errors that variables set previous do not exist.
The code loops through the subfolders (q1, q2, etc.) under the directory. For each subfolder, there is another for loop to set several variables. I echoed three variables fine in loops.
However, when using a batch called abc.rb, the error is COM_M does not exist.
Actually, the error is all three variables do not exist.
setlocal ENABLEDELAYEDEXPANSION
for /f %%f in ('dir /ad /b ') do (
echo %%f
pause
pushd %%f
for %%a in (*.a*.dat) do (
set COM_DATA=%%a
echo !COM_DATA!
set COM_V=%%f\com-v.dat
echo !COM_V!
set COM_M=%%f\com-M.dat
echo !COM_M!
)
chdir
set fig=someA
set matrix=someB
rem use a written batch (called abc.rb) to process data
abc.rb -a !COM_DATA! -b !COM_V! -c !COM_M! -d !fig! -e !matrix!
popd
)
endlocal
Can anyone find any bugs? Thank you!
I am not sure why the need to pushd into the dir, but as far as I can see, there is only a need for a single for loop:
#echo off
setlocal enabledelayedexpansion
set "fig=someA"
set "matrix=someB"
for /R %%a in (*.a*.dat) do (
set "COM_DATA=%%a"
echo !COM_DATA!
set "COM_V=%%~dpacom-v.dat
echo !COM_V!
set COM_M=%%~dpacom-M.dat
echo !COM_M!
rem If abc.rb is is NOT a windows batch file, remove call below
call abc.rb -a "!COM_DATA!" -b "!COM_V!" -c "!COM_M!" -d !fig! -e !matrix!
)
If you require the pushd (which I doubt)
#echo off
setlocal enabledelayedexpansion
set "fig=someA"
set "matrix=someB"
for /R %%a in (*.a*.dat) do (
pushd "%%~dpa"
set "COM_DATA=%%a"
echo !COM_DATA!
set "COM_V=%%~dpacom-v.dat"
echo !COM_V!
set "COM_M=%%~dpacom-M.dat"
echo !COM_M!
rem If abc.rb is is NOT a windows batch file, remove call below
call abc.rb -a "!COM_DATA!" -b "!COM_V!" -c "!COM_M!" -d !fig! -e !matrix!
popd
)
The double quotes will help if the paths have whitespace, if your program has an issue with them, then you can remove them: abc.rb -a !COM_DATA! -b !COM_V! -c !COM_M! -d !fig! -e !matrix!
#echo off
setlocal ENABLEDELAYEDEXPANSION
set "fig=someA"
set "matrix=someB"
set "COM_V=com-v.dat"
set "COM_M=com-M.dat"
for /f %%f in ('dir /ad /b') do (
echo %%f
pause
if exist "%%~f\*.a*.dat" (
pushd "%%~f" && (
for %%a in (*.a*.dat) do (
set "COM_DATA=%%~a"
echo !COM_DATA!
)
chdir
rem use a written batch called abc.rb to process data
call abc.rb -a "!COM_DATA!" -b "!COM_V!" -c "!COM_M!" -d "!fig!" -e "!matrix!"
popd
)
)
)
endlocal
Issues:
If the nested for loop finds no files with the matched pattern of *.a*.dat, then the variables COM_DATA, COM_V and COM_M may not be defined or updated with a newer value.
Value of COM_DATA is a filename. Values of COM_V and COM_M is the parent folder name and filename, which is inconsistent. Based on the current directory, I would consider filenames as correct. This means that COM_V and COM_M never need to change.
If abc.rb is a batch-file, then you need to use call for the interpreter to return control back to the main script.
Changes:
Test if the file pattern exists, and run the code within the code block if true.
COM_V and COM_M moved out of the for loop as values never change.
Calling abc.rb as being a batch-file.
fig and matrix moved out of the for loop as values never change.
Double quote setting of variables and use of variables to avoid issues with spaces, special characters etc.
pushd && ( ensures the code within the parentheses is run only on success of changing directory.
Removed parentheses in the rem line. They may not cause a problem, though rem lines are parsed and can cause a syntax error. Suggest avoiding special characters in rem lines unless you intend to debug.
Is there a way I can use a set command to isolate a few words from a variable given and then save that as another variable to use later?
If I say cd C:\users, can I take something like the C:\users and save that as a variable?
:inputstage
set /p input=Enter command:
if /i {%input%}=={get} (goto :get)
:get
set %input%==%getfile%
echo %getfile% >>Files to get.txt
call getfiles.bat
goto: inputstage
I input the following when it asks me to enter a command: "get index.html" and it then will take index.html and save it to a file called "files to get.txt" then it'll call up a piece of code that will run the command ftp -s:ftpreceive.bat that ftp receive file will then go and read the "files to get.txt" and see index.html and place that into the following ftp code get index.html(or whatever file I specify)
Can I get only a part of the input variable to become the variable getfile?
you seem to try to separate words from input or separate into <command> and <whatever>. In both cases, for is the way to go:
#echo off
setlocal enabledelayedexpansion
set "input=get something special"
echo method 1 (get "first word" and "rest"):
for /f "tokens=1*" %%a in ('echo %input%') do (
set "command=%%a"
set "param=%%b"
)
echo %command% : %param%
echo/
echo method2 (get every word):
set i=0
for %%a in (%input%) do (
set "_var[!i!]=%%a"
set /a i+=1
)
set _var
AIMS:
Learn how to use empty variables in a FOR LOOP routine in a batch file.
PROBLEM:
I am using whether a variable is defined or not to determine the appropriate subroutine in my script; 2 variables defined == go do something. One is defined but not the other == do something else. Both not defined == something else again.
The script checks for C$ access and the presence of a certain file within the C$ of a text file list of networked PC's. If both criteria are met by 2 variables having data set, a simple xcopy updates the file in question by going to a certain subroutine and then the LOOP moves to the next PC on the network to try and do the same.
If there is an issue I want to use the variables in an empty state to then do something else; for example report to text file that c$ was not accessible or the file was missing meaning bad install etc.
However on my 2 test machines I am breaking the folder paths to trip the error reporting routines and finding something strange I can't fix without writing more lines of code. In my text file I have 2 PC's listed a few times e.g.
PC1
PC2
PC1
PC2
PC1
PC2
PC1 has a broken file path to test error logging
PC2 All fine to test file update process
When I run the script PC1 gets reported as having a problem and logs correctly.
PC2 all is fine and the update happens fine. BUT THEN it hits PC1 again, but seems to think [even though file path still broken] that it is OKAY --as if the variable is remembered from the previous loop and of course tries to update and has problems.
Here is the code I was trying to get to work using empty variable
#echo off
color 0E
pushd %~dp0
setlocal EnableDelayedExpansion
for /f "usebackq tokens=*" %%i in ("%~dp0hostnames.txt") do (
rem Test Access Admin Shares C$
if exist "\\%%i\C$\Windows\System32" set dollar=yes
rem Test Installation Integrity
if exist "\\%%i\C$\ProgramData\config.cfg" set install=ok
echo %%i
echo !dollar!
echo !install!
pause
IF !dollar!==yes IF !install!==ok (call :updatecfg %%i)
IF !dollar!==yes IF [!install!]==[] (call :installerror %%i)
IF [!dollar!]==[] (call :errorshare %%i)
)
echo THE END
pause
exit
:updatecfg
CLS
XCOPY "%~dp0config.cfg" /Y "\\%1\C$\ProgramData" & echo %1 Update Config.cfg Succeeded! & echo %1 Update Succeeded! >>"%~dp0logpass.txt"
ping 127.0.0.1 -n 3 >nul
goto :eof
:errorshare
CLS
echo.
echo %1 Has C$ Access Issues [Logging] & echo %1 Has C$ Access Issues >>"%~dp0logfail.txt"
ping 127.0.0.1 -n 3 >nul
goto :eof
:installerror
CLS
echo.
echo %1 Cannot Find Config.cfg^!^! [Logging] & echo %1 Cannot Find Config.cfg^!^! Not Installed or Configured^? >>"%~dp0logfail.txt"
ping 127.0.0.1 -n 4 >nul
goto :eof
If I add if not exist entried to the 2 at the start and have them set something when there is a problem then this works fine. But I'd like to know if this the right way to do this or should I also be able to use empty variables. I am nearly there it's just that they are not clearing properly per loop.
Many thanks.
....
for /f "usebackq delims=" %%i in ("%~dp0hostnames.txt") do (
rem Clear variables for each iteration
set "dollar="
set "install="
rem Test Access Admin Shares C$
if exist "\\%%i\C$\Windows\System32" set "dollar=yes"
rem Test Installation Integrity
if exist "\\%%i\C$\ProgramData\config.cfg" set "install=ok"
if defined dollar (
if defined install (
call :updatecfg %%i
) else (
call :installerror %%i
)
) else (
call :errorshare %%i
)
)
....
or
....
for /f "usebackq delims=" %%i in ("%~dp0hostnames.txt") do (
rem Test Access Admin Shares C$
if exist "\\%%i\C$\Windows\System32" ( set "dollar=yes" ) else ( set "dollar=" )
rem Test Installation Integrity
if exist "\\%%i\C$\ProgramData\config.cfg" ( set "install=ok" ) else ( set "install=" )
if defined dollar (
if defined install (
call :updatecfg %%i
) else (
call :installerror %%i
)
) else (
call :errorshare %%i
)
)
....
In any case, you should ensure the variables have the adecuated value before taking a decision based on their content.
Been wrecking my brain all night trying to figure out why this isn't working, but one of my variables isn't releasing on the next iteration of my loop and I can't figure out why... The first pass of the loop seems to work fine, but the next iteration, the first variable gets locked and the script connects to the system that's already been configured.
I've been staring at this for a while now and no matter how I approach it, it still behaves badly. :/ The purpose is to read a text-string of a given file, and use it to modify (via Find and Replace (fnr.exe)) another file with several instances of the required data. I didn't have alot of luck with 'findstr' replacing so many instances of the text required so I went with a tool I've used before that seemed to work really well in it's previous scripting application...
Truth be told, I find myself stumbling with even the most basic code a lot of times, so any kind soul willing to impart some wisdom/assistance would be greatly appreciated!
Thanks in advance...
#ECHO ON
setlocal enabledelayedexpansion
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET lwn=
SET WKSTN=%%A
rem connect to workstation and read lwn.txt file
pushd "\\%WKSTN%\c$\"
IF ERRORLEVEL 0 (
FOR /F %%I in (\\%wkstn%\c$\support\lwn.txt) DO (
SET LWN=%%I
%~dp0fnr.exe --cl --dir "\\%WKSTN%\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF ERRORLEVEL 0 ECHO Station %LWN%,Workstation %WKSTN%,Completed Successfully >> %~dp0report.log
IF ERRORLEVEL 1 ECHO Station %LWN%,Workstation %WKSTN%, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
popd
)
)
IF ERRORLEVEL 1 (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
set wkstn=
set lwn=
echo pop d complete
)
msg %username% Script run complete...
eof
The ! notation must be used on all variables that are changed inside the loop.
C:>type looptest.bat
#ECHO OFF
setlocal enabledelayedexpansion
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET WKSTN=%%A
ECHO WKSTN is set to %WKSTN%
ECHO WKSTN is set to !WKSTN!
pushd "\\!WKSTN!\c$\"
ECHO After PUSHD, ERRORLEVEL is set to %ERRORLEVEL%
ECHO After PUSHD, ERRORLEVEL is set to !ERRORLEVEL!
IF !ERRORLEVEL! NEQ 0 (
ECHO ,,SYSTEM IS OFFLINE
) ELSE (
ECHO Host !WKSTN! is available
)
popd
)
EXIT /B 0
The workstations.txt file contained the following. (I should not give out actual host names.)
LIVEHOST1
DEADHOST1
LIVEHOST2
The output is...
C:>call looptest.bat
WKSTN is set to
WKSTN is set to LIVEHOST1
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST1 is available
WKSTN is set to
WKSTN is set to DEADHOST1
The network path was not found.
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 1
,,SYSTEM IS OFFLINE
WKSTN is set to
WKSTN is set to LIVEHOST2
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST2 is available
Although your code have several issues, the main one is the use of % instead of ! when you access the value of variables modified inside a for loop (although you already have the "enabledelayedexpansion" part in setlocal command). However, I noted that you sometimes use the FOR replaceable parameter (like in --replace "%%I") and sometimes you use the variable with the same value (%LWN%), so a simpler solution in your case would be to replace every %VAR% with its corresponding %%A for parameter.
I inserted this modification in your code besides a couple small changes that make the code simpler and clearer.
#ECHO ON
setlocal
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem Read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
rem Connect to workstation and read lwn.txt file
pushd "\\%%A\c$\"
IF NOT ERRORLEVEL 1 (
FOR /F "usebackq" %%I in ("\\%%A\c$\support\lwn.txt") DO (
%~dp0fnr.exe --cl --dir "\\%%A\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF NOT ERRORLEVEL 1 (
ECHO Station %%I,Workstation %%A,Completed Successfully >> %~dp0report.log
) ELSE (
ECHO Station %%I,Workstation %%A, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
)
)
) ELSE (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
echo pop d complete
)
msg %username% Script run complete...