I want to create a bat file with set variables from another bat file.
This is for a startup .bat file that maps network drives and copies some files to the local computer and looks at the system services that needs to run.
It also needs to create a log file every time the logon .bat file runs. Here is a sample what I have done.
ECHO set SERVER IP=>>"V:\GENESIS\GENESIS INSTALL FILES\GenesisLogonUser.bat"
ECHO set DRIVE1=V>>"V:\GENESIS\GENESIS INSTALL FILES\GenesisLogonUser.bat"
ECHO set MAPDRIVE1=\\%SERVER IP%\v /P:Yes>>"V:\GENESIS\GENESIS INSTALL FILES\GenesisLogonUser.bat"
ECHO net use %DRIVE1%: %MAPDRIVE1% >>"%userprofile%\Documents\scripts\logonlog.txt">>"V:\GENESIS\GENESIS INSTALL FILES\GenesisLogonUser.bat"
You need to escape percent expansion of the variables. This can be done by doubling each % sign in a batch script:
set "VAR=Value"
echo %%VAR%%
This will echo:
%VAR%
Note that this does not work directly in the console window; you need to do it like this instead:
>set "VAR=Value"
>echo ^%VAR^%
%VAR%
So to apply this to your script, it looks like this (I excluded %USERPROFILE% from the escaping as I do not know how you like it; of course you could write %%USERPROFILE%% instead as well):
(
ECHO set "SERVER IP="
ECHO set "DRIVE1=V"
ECHO set "MAPDRIVE1=\\%%SERVER IP%%\v /P:Yes"
ECHO net use %%DRIVE1%%: %%MAPDRIVE1%%^>^>"%USERPROFILE%\Documents\scripts\logonlog.txt"
) > "V:\GENESIS\GENESIS INSTALL FILES\GenesisLogonUser.bat"
Since there is some redirection in the echoed text, this needs to be escaped like ^>^>.
I also put all ECHO commands within parentheses, which requires a single redirection operation only; this improves legibility and performance.
In addition, I improved the set syntax so that the entire assignment expression is placed in between a pair of quotation marks, which make it robust against special characters (the quotes do not become part of the value).
Related
I'm creating a batch script to a get a file based on what option the user chooses. The only problem is, the file in the ftp server has a colon and from what i've researched, windows does not accept ":" colon.
Is it possible to replace that character before downloading?
Below is a sample of my code.
Echo open sample.net>first.dat
Echo user>>first.dat
Echo password>>first.dat
Echo ascii>>first.dat
Echo cd directory>>first.dat
Echo lcd folder>>first.dat
Echo get sample-text-10-16-2017_16:36:00:340033.txt>>first.dat
Echo bye>>first.dat
ftp -v -i -s:first.dat
del first.dat
As you can see also, I get a list first of the file names inside the folder for the user to input the file name. I just wrote a specific file name for the example
I'm still not familiar with the for loops in batch but I think that it is one way of replacing the characters in a file name before downloading
[Untried]
get remotefilename localfilename
is apparently valid, so placing a valid windows filename as a second argument should d/l to the file specified.
[Addendum - also untried]
(after Echo lcd folder>>first.dat)
echo mls remotefilesrequired awindowlistfilename>>first.dat
rem this should log in and create awindowslistfilename
rem containing the remote filelist
ftp -v -i -s:first.dat
del second.dat 2>nul
setlocal enabledelayedexpansion
for /f "delims=" %%a in (awindowslistfilename) do (
set "remote=%%a"
echo get !remote! !remote::=.!>>second.dat
)
endlocal
Echo bye>>second.dat
ftp -v -i -s:second.dat
del first.dat
del second.dat
Since I'm not aware of the return format for mls, I'm assuming that it's a simple file-list, one to a line.
This code first executes the ftp log-on palaver and an mls command, creating awindowslistfile locally.
It then deletes second.dat (the 2>nul suppresses error messages like file not found appearing on stderr)
setlocal enabledelayedexpansion and endlocal bracket a mode where the syntax changes such that !var! may be used to access the run-time value of a variable, whereas %var% always refers to the parse-time value.
The for/f command reads the filename (parenthesised) and assigns each line in turn to the metavariable %%a. The delims= option ensures that the entire line is assigned, ignoring the normal tokenising procedure.
A series of individual get commands is then written to second.dat, with the substitution of : by . in the name.
Finally, add the bye and FTP again.
(I'm not sure whether first.dat will also require a bye and second.bat will need to prelimnary commands, but could be...)
Note that it's batch convention to enclose filenames that may contain separators like Space,; in "quotes". How FTP will feel about that, if necessary, I can only guess.
Naturally, extra lines within the loop
set "remote=!remote:x=y!"
could be used to serially replace character sequences x by y if there are any other problematic characters encountered.
I have a scripting system driven by powershell which calls BAT files
I want to record the Envs at the end of the BAT session so that they are available for the next BAT file. I have found what I did, did not capture changes from the BAT file...
More
I had code like
& cmd.exe "/c $script $optionalArgs & (echo Name^=Value&SET) > ""c:\path\end_envs.csv"""
$script == dosomework.bat
which appeared to work, i.e. the csv file is created with env's BUT does not have the ones created by $script... Rather seem to be the ENVs of the initial cmd.exe call...
After adding the 'echo xx' lines to a BAT file and calling this at the bottom of the bat file referenced in $script, all is OK, I see the env at the end of the $script BAT file
Is there a way to fool a cmd.exe /c to consider my extra echo info as part of the called script ?
Thanks
OR...
sae this as $$script.bat
#ECHO OFF
(
echo Name=Value
CALL $script %*
SET
)>"c:\path\end_envs.csv"
and modify your code to like
& cmd.exe "/c $$script $optionalArgs"
whee $$script.bat may naturally be any name you like.
Note that this assumes that $script.bat (the one you are actually executing) does not contain a setlocal instruction.
If $script is variable, then in the batchfile $$script.bat try
#ECHO OFF
SETLOCAL
(
echo Name=Value
set "params=%* "
CALL %1 %%params:* =%%
set "params="
SET
)>"u:\envs.csv"
GOTO :EOF
This assumes there are no confounding factors (parameters containing characters to which cmd is sensitive, for instance - normal alphameric strings should be fine)
So
write out the header line
set params to the supplied parameter list + a space
execute a subroutine firstparameter with parameter(s) remainingparameters. The call ...%%params:* =%% syntax means "the value of params, up to and including the first space"
clear params
list the remining environment variables.
The destination filename is of course up to you. I used a name that's convenient for my system.
So - you would call this script with a parameter of (the first name of the script you want to run) + (any other parameters you require)
Simplest would probably be to add
(echo Name^=Value
SET) > "c:\path\end_envs.csv"
to the end of $script (or at least, force those two lines to be executed just prior to exit)
I have an archive.pst file on my C: drive that I use in outlook to backup my email. But my C: is not backed up each night. So I'd like to copy the .pst file to my network drive so it will consistently be backed up. I do not want outlook to open the .pst file directly from the network drive for a variety of reasons.
Therefore I am trying to create a scheduled task that will copy my .pst file to a network location each day. The batch file below works perfectly if double-clicked. If I try to run the scheduled task, only the log file is created. Outlook doesn't close and the .pst file is not copied. I've tried running with the highest privileges but that doesn't seem to help. Any ideas would be appreciated.
cscript.exe close_outlook.vbs
::This is my VBS Script
::Set Outlook = CreateObject("Outlook.Application")
::Outlook.Quit
ping localhost > nul
set idrive="\\myserver\drive\\Outlook Files\"
set current="C:\myfolder\myuser\Documents\Outlook Files"
echo Start Time of Copy: %time% >> %idrive%\Log.txt
copy %current%\archive.pst %idrive%\archive.pst /y
echo End Time of Copy: %time% >> %idrive%\Log.txt
move %idrive%\Log.txt %idrive%\BackupLogs\Log.txt
ren %idrive%\BackupLogs\Log.txt %date:~10,4%-%date:~4,2%-%date:~7,2%_log.txt
cscript.exe open_outlook.vbs
::This is my VBS Script
::set shell = createobject("wscript.shell")
::shell.run "outlook.exe"
EXIT
In reviewing the previous responses, I have shortened the batch file to only the code below. This works when double-clicking, but not when scheduling a task. I've also tried the same task moving the .vbs script to a network drive. Same outcome.
%SystemRoot%\System32\cscript.exe "C:\OutlookBackup\close_outlook.vbs"
%SystemRoot%\System32\ping.exe -n 4 127.0.0.1>nul
%SystemRoot%\System32\cscript.exe "C:\OutlookBackup\open_outlook.vbs"
1. Specify all files in a batch file executed as scheduled task with full path.
Double clicking on a batch file results usually in running the batch file with currently working directory being the directory of the batch file. But when running a batch file as scheduled task, the system32 directory of Windows is the current working directory.
Is close_outlook.vbs and open_outlook.vbs in system32 directory of Windows?
I don't think so. Replace in batch code below Path to\Script File twice by the right path.
2. Assign string values with space to environment variables right.
variable=value is the parameter for command set. With
set idrive="\\myserver\drive\\Outlook Files\"
you assign to variable idrive the value "\\myserver\drive\\Outlook Files\" with the double quotes included. This results on expansion of
echo End Time of Copy: %time% >> %idrive%\Log.txt
in the command line
echo End Time of Copy: 19:21:53 >> 1>"\\myserver\drive\\Outlook Files\"\Log.txt
and this is not right, isn't it.
Correct is:
set "idrive=\\myserver\drive\Outlook Files"
I removed also second backslash after drive and the backslash at end of folder path.
As the environment variable contains now the path with space(s) without double quotes, the double quotes must be added where the value of the environment variable is used concatenated with a file name, see batch code below.
There is one more reason why using "variable=value". A not visible trailing space at end of the line with command set in batch file is also appended to value of the environment variable if double quotes are not used or used wrong. Read this answer for details about correct assignment of string values to environment variables.
3. Define the wait loop using command ping better.
The command
ping localhost > nul
produces a wait. But it is better to use something like
%SystemRoot%\System32\ping.exe -n 4 127.0.0.1>nul
as now the wait is determined with exactly 3 seconds.
4. Do not insert a space left of redirect operators > or >> for stdout.
Why this should not be done was explained by me here in detail.
5. Avoid environment variables not defined in batch file itself or in system account.
Your batch file uses only environment variables defined in batch file itself. So this advice is here not really needed.
However, many batch file working fine on double click but not on running as scheduled task fail because the batch file depends on environment variables like PATH or others which are related to current user account. It is safe to use environment variables which exist for all accounts like SystemRoot.
Reworked batch code
Here is your batch file with the appropriate changes whereby the paths of the two *.vbs files must be set correct by you before the batch file (hopefully) works as scheduled task.
%SystemRoot%\System32\cscript.exe "Path to\Script File\close_outlook.vbs"
%SystemRoot%\System32\ping.exe -n 4 127.0.0.1>nul
set "idrive=\\myserver\drive\Outlook Files"
set "current=C:\myfolder\myuser\Documents\Outlook Files"
echo Start Time of Copy: %time%>>"%idrive%\Log.txt"
copy /B /Y /Z "%current%\archive.pst" "%idrive%\archive.pst"
echo End Time of Copy: %time%>>"%idrive%\Log.txt"
move "%idrive%\Log.txt" "%idrive%\BackupLogs\Log.txt"
ren "%idrive%\BackupLogs\Log.txt" %date:~10,4%-%date:~4,2%-%date:~7,2%_log.txt
%SystemRoot%\System32\cscript.exe "Path to\Script File\open_outlook.vbs"
set "idrive="
set "current="
I'm trying to write a script that will search for all file recursively in a folder and run a command and/or list of commands on each file. Here's an example of how I need it to work:
Search folder for files containing "1080p" in the file name
Copy this file to a local directory but remove the "1080p" from the filename
Run another batch script on those files (I already have that part working)
All this needs to be automated.
I need to do this in the Windows command prompt and I'm willing to use any other programs required.
I'm already using SED in a similar batch script.
#echo off
setlocal disableDelayedExpansion
set "src=sourcePath"
set "dst=destinationPath"
set "search=1080p"
for /r "%src%" %%F in (*%search%*) do (
set "full=%%~fF"
set "name=%%~nxF"
setlocal enableDelayedExpansion
copy "!full!" "%dst%\!name:%search%=!"
endlocal
)
REM call your batch script here to process the copied files
You could have problems if the same filename exists in multiple source folders. But that is a general problem with your stated requirements.
Explanation:
%%~fF gives the full path to the file contained in %%F
$$~nxF gives the name and extension only of the file contained in %%F
type HELP FOR or FOR /? for more information about the modifiers available for FOR variable expansion.
!name:%search%=! uses delayed expansion to search the contents of name and replace the search value with nothing. In this example %search%=1080p. Note that the search is not case sensitive.
I need to use delayed expansion when doing search and replace within the loop because normal expansion using percents occurs when the statement is parsed. But the entire FOR construct, encluding the contents of the parentheses, is parsed as one logical statement. So normal expansion would give the value of name prior to the loop executing. That won't work :-) Delayed expansion gives the current value each time the line is executed.
Type HELP SET or SET /? for more information about search and replace and delayed expansion.
I need to toggle delayed expansion on and off because ! is a valid character in a filename, and %%F expansion will corrupt the value if it contains !.
I would recommend using cygwin to easily take advantage of some common Unix utilities, although you can probably locate windows compiled versions one by one (i.e. perl):
Find a list of files by name, execute a command on each:
find . -name \*1080p\* | xargs <command>
Find a list of files, create a list of move/rename commands to strip out '1080p' string:
find . -name \*1080p\* | perl -ne 'chomp();$x=$_;s/1080p//g;print"cp $x /target/$_\n"'
# add "| sh" to run the commands, or "> commands.sh" to generate a script
Command that should run in CMD only requiring PERL
dir /s /b | perl -ne 'chomp();$x=$_;s/1080p//g;print"copy $x c:\target\$_\n"' > cmds.bat
Adjust the scripts, of course, to do what you need. However, I believe this will accomplish your goal. There may be a clever way to do this with Windows CMD, but I am not a CMD expert. Unix utilities are (IMHO) much more straightforward!
How can I run a command in my dos script from an input argument ?
Simplified script bla.bat:
CALL %1
Call it:
bla.bat "echo 'hello'" (or bla.bat "git status")
Error:
'"git status"' is not recognized as an internal or external command,
operable program or batch file.
It works if I do "CALL git status".
The important thing to remember is that the expanded text must look exactly like it would if you were to simply key in the command from the command line. (actually there are a few exceptions, but this is a good starting point).
To debug your script, simply put an echo before your call: #echo call %1. Now try running as you did earlier: blah.bat "echo 'hello'" produces call "echo 'hello'". Try running that from the command line - it doesn't work. You want call echo 'hello'.
One fix would be to change your script slightly: The ~ modifier strips enclosing quotes from the argument
#echo off
call %~1
Or you might be able to ditch the call and simply use the following (as long as you are not calling another batch file from which you want to return)
#echo off
%~1
If there are no other arguments on the command line, you might be better off using %* which expands to all the arguments
#echo off
%*
REM or call %*
Now you can call your batch like so
blah.bat echo "hello"
Be aware that batch has all kinds of special case weirdness that will likely require extra or different coding to work around. Too many to list - just expect the unexpected.
It looks like the problem may be that you have surrounding quotes in your input, which you'll need to stop it being broken into the different %n arguments, but try:
%~1 Which will strip any surrounding quotes from the input.
%~$PATH:1 which will strip any surrounding quotes then search within the $PATH env-var for the first match, then expand the string to include the full path to the command, which won't work for git using the windows distro because git is a batch file, and cmd would look for git status.bat
If its to be used with git, you may as well use %~1 and %~2 to call git then provide the argument to the git batch file, or call git.exe directly by modifying your $PATH. But remember that git.bat does some enviroment setup of its own before calling git itself.
I think you'll need %1% to echo the parameters. Here's my lame script which I think does what you want, works with your echo test:
bla echo hello
Gives:
C:\tmp>echo bla
bla
C:\tmp>echo echo
echo
C:\tmp>CALL echo hello
hello
echo %0%
echo %1%
CALL %*
If you want to parse through the command line arguments, let me know.
The problem is the spaces between the parameters are throwing you off (which is why you were using the quotes around git status).
Modify your bla.bat to iterate through your command line paremeters. This works for me:
SETLOCAL ENABLEDELAYEDEXPANSION
SET VAR1=
FOR %%A IN (%*) DO (
SET VAR1=!VAR1! %%A
)
call %VAR1%
ENDLOCAL
Then, run your bla.bat without the quotes around git status.
bla git status
Essentially, what this does is iterate through your command line parameters, and then executes them all as one command. The challenge comes from FOR loops in DOS not allowing you to use a variable that you're setting within itself, so you need to enable "delayed expansion" of variables. Then, the variable that you're setting needs to be encapsulated in exclamation points (not %'s). And of course, the space between !VAR1! and %%A keeps the parameters from running together in the CALL.