====== Windows Scripting ====== ===== Windws vs. Linux Scripting ===== Windows shell scripting is similar to Linux shell scripting in many ways. However, since Windows and Linux (and other Unix-like operating systems) have different technical heritages, some of the syntax and approaches are very different. ==== An Abundance of Shells ==== We've looked at scripting on Linux systems using the //bash// shell -- which is the most widely-deployed shell on Linux and other Unix-like systems. There are many other shells on similar systems, including:​ * sh – the original Unix shell ("Bourne Shell"), written by Steven Bourne​ * ksh – the Korn shell, written by David Korn​ * csh – the C shell, which has a C-like syntax​ * fish – the Friendly Interactive shell​ * Zsh – the Z shell, similar to ksh​ Many of these have a syntax based on and similar to the original Bourne shell, which is standardized in the POSIX.1 standard (or IEEE Standard 1003.1), and these shells have a lot in common.​ On Windows, there are two main shells: * CMD - The Windows command shell, which is the traditional Windows shell. Although based on traditional DOS command syntax, the CMD shell has been considerably expanded, with many new features added over the last few years.​ * PowerShell – this is a new Windows command shell, which combines scripting with object-oriented programming. It is pre-installed for interactive use on current Windows systems; however, the execution of PowerShell scripts is disabled by default on all Windows "client" (non-server) systems, including Seneca lab computers.​ ​ Due to PowerShell scripting being disabled by default, and because object oriented programming is not taught in the first semester programming courses, we will focus on scripting using the traditional Windows CMD shell.​ ==== Cross-Platform Scripting ==== There are several possible approaches to writing scripts that will work on both Linux and Windows systems, as well as other common operating systems such as Mac OS:​ * Bash or Zsh – bash is the default shell on most Linux systems, and zsh is the default shell on most Mac OS systems; either shell can be easily used on either system. Both shells are available as third-party add-ons for the Windows operating system, usually shipped with a collection of GNU utilities compiled for use with Windows (for example, see https://gitforwindows.org/)​ * PowerShell – installed by default on current Windows systems, it is also available for Linux and Mac OS systems, although it is not commonly used on those systems yet (see https://github.com/PowerShell/PowerShell)​ ==== Other Interpreted Languages ==== In addition to shell scripting languages, there are other interpreted languages that are well suited for cross-platform development, including Python and Perl.​ ​When should you use a shell scripting langauge?​ * Shell scripting languages are ideally suited for process control – executing, managing, and combining external programs to accomplish tasks.​ * Shell scripting languages are not well-suited for implementing advanced processing algorithms, because they generally lack features such as good floating-point support, typed variables, and strong support for large arrays and hashes.​ ==== Windows "Shell Scripts" vs. Batch Files" ==== DOS and early Windows systems were inherently interactive in nature, and early scripts were called "batch files" because they were viewed as similar to non-interactive batch processing on mainframe systems.​ This terminology has stuck, and Windows shell scripts are still often called "batch files" (hence the occasional use of the .bat extension instead of .cmd).​​ The concept of a "batch file" and a "shell script" are roughly equivalent.​ ​===== Basic Requirements for Shell Scripts ===== Remember these requirements? They apply to Windows scripts too: 1. **Create a text file containing shell commands.​** Use any text editor, such as Notepad, to create the file. 2. **Tell the operating system which shell to use to execute the commands.​** In Windows, the filename extension is used to associate a file with a program, and this mechanism is used to associate a script with a command interpreter.​ ​ * For CMD scripts, the extension ".cmd" is used.​ For historical reasons, the extension ".bat" is also accepted.​ * For PowerShell scripts, the extension ".ps1" is used.​ The reason that ".ps" isn't used is that that extension was already used for PostScript files.​ (As a reminder: We're not going to write PowerShell scripts in this course).​ 3. **Ensure that the script file has the appropriate permissions.​** On Windows, the ability to read the script file is sufficient (and this is the default permission, so no change is usually required for scripts that you create for your own use; the situation may be different for scripts that are shared to other machines over the network or to other users on your system).​ ===== Command Echos​ ===== Windows defaults to displaying each command in a script before executing it (the opposite of the default in the bash shell). If you do not want each command to be displayed, you can:​ * Add an @ sign in front of each command, or​ * Issue the echo off command.​ Usually, you'll combine these in a script, using this as one of the first lines:​ @echo off​ ===== A Basic Script Example ===== Here is a simple example script using two commands, ''echo'' and ''date'': @echo off echo The current date is: date /t If this is save into the file named "now", the permission could be set with this command: $ chmod u+rx now The script can then be executed. Normally, the current working directory is not searched, so to run the a script in the current directory, you will need to explicitly specify the directory name like this: $ ./now The current date is: 2024-12-11 ===== Variables ===== ==== Setting a Variable ==== To set a variable, use the ''set'' keyword with a variable name, an equal sign, and the variable value: set A=5 set B=World If the variable does not exist, it will be created. If it does exist, the previous value will be discarded. Variable names may contain letters, digits, or underscores, but must not start with a digit. Case does not matter! The variable names ''Number'', ''number'', and ''NUMBER'' all refer to the same variable. Do not put spaces on either side of the equal sign. Variables are not typed -- they may be used as strings, integers, or decimal values. ==== Accessing a Variable ==== To access a variable, place a percent sign [%] on either side of it, and use it in a command as an argument (or as a command name): > SET B=World > echo %B% World > echo Hello $B Hello World ==== Quoting ==== === Word Splitting and Quoting === Quoting in the Windows shell is very different from Bash! Using single or double quotes causes the quotes themselves to be included as part of the string or argument in most cases, but not when dealing with a filename:​ > ECHO "Hello"​ "Hello"​ ​ > ECHO test > "test file"​ ​ Quoting is not required when assigning a string value which contains spaces to a variable: > SET A=One Two Three > ECHO %A% One Two Three ==== Carat Symbols ==== Escaping characters to remove their special meaning is performed using the carat [^] symbol in Windows.​ In this example, the ampersand [&] symbol would normally cause an error, but it can be treated as a regular character by escaping it with a carat: > echo Lost ^& Found Lost & Found When piping, a CMD subshell is started for each command in the pipeline, and it is necessary to use triple carat symbols ^^^ to escape characters:​ > echo Lost ^^^& Found | find "Lost" ​ Lost & Found ​ ===== Environment Variables ===== By default, all variables are environment variables, inherited by child processes.​​ Environment variables are commonly used to pass configuration information to programs and to configure how programs operate.​ You can view all of the current variables with the ''set'' command; you'll probably want to pipe the output through ''more''. ​ Environment variables are used by all processes, not just the shell!​ ==== Common Environment Variables ==== ^ Environment Variable ^ Purpose ^ Examples ^ | CD | Current directory | ''ECHO %CD%'' | | TIME | Current time (HH:MM:SS) | ''ECHO %TIME%'' | | DATE | Current date in local format | ''ECHO %DATE%'' | | ERRORLEVEL | The error code / exit status of the last command executed (note that some commands or programs do not set this variable as expected). | ''DIR \FileThatDoesNotExist ''\\ ''ECHO %ERRORLEVEL%'' | | PATH | A semicolon [;] separated list of directories that will be searched when looking for a command | ''PATH="$PATH;\SpecialDirectory"'' | PROMPT | The prompt presented by the shell. | ''SET PROMPT=Enter a command: ''\\ ''SET PROMPT=$P$G '' | | RANDOM | A random integer (0-32767) | ECHO %RANDOM% | Note that the ''prompt'' and ''path'' programs may also be used to adjust the PROMPT and PATH environment variables, respectively. ===== Reading Variable Values from STDIN: SET /P ===== You can read values from standard input (stdin) and assign them to a variable with the set command using the /p option (aka switch). When you do this, the value specified on the right-hand side of the equal sign is used as the prompt presented to the user: > set /p NAME=Enter your name: Enter your name: J. Doe > echo %NAME% J. Doe Here is a script which uses a couple of ''SET /P'' statements: @echo off​ set /p NAME=Please enter your name:​ echo Please to meet you, %NAME%​ set /p FILE=Please enter a filename:​ echo Saving your name into the file...​ echo NAME=%NAME% >> %FILE%​ echo Done.​ ==== Command Capture ==== There is no direct equivalient to command capture, but it is possible to use ''SET /P'' with redirection //from// standard input using the less-than [<] symbol to capture one line of text: > DATE /T >X > SET /P D= ECHO %D% 2024-12-11 ===== Arithmetic ===== CMD can perform __integer__ arithmetic. To evaluate an arithmetic expressions and store the results in a variable, use the ''SET'' command with the ''/A'' (arithmetic) option. (Note: when used interactively the result of the expressions evaluation will be output to stdout; this does not happen inside scripts). > SET A=100 > SET B=12 > SET /A X=A*B 1200 <--- This does not get printed inside a script > ECHO %X% 1200 > SET /A A+=1 > NUL: > ECHO %A% 101 > SET /A C=A*B*2 > NUL: > ECHO The answer is %C% The answer is 2424 Notes: * You can perform more than one arithmetic evaluation and assignment in one SET command by separating the expressions with a comma [,] * Some characters used in arithmetic expressions, such as the carat symbol, may need to be quoted or escaped to function correctly.​ * Percent signs, when used in arithmetic expressions (as the modulo operator), need to be doubled [%%] to avoid confusion with the percent signs placed around variable names​ ===== Conditional Logic: IF/ELSE ===== The ''IF'' command takes a test, and uses the result of the test to control the execution of one or more commands. An ''ELSE'' clause is optional; if included, the first conditional commands should be placed in parenthesis.​ > set A=Blue ​ > set B=Orange​ > set C=Blue​ ​> if %A%==%C% echo Strings A and C match​ Strings A and C match​ > if %A%==%B% (echo Same!) else echo Different!​ Different! === Available Tests === There are four main types of tests available: == Tests Group 1: Filesystem Entries == Tests that a filename exists (regardless of the entry type: file or directory): EXIST filename == Tests Group 2: String Equality == Test for string equality: ''string1''==''string2'' == Tests Group 3: String and Numeric Comparisons == These tests accept two string arguments, both strings or both integers, which are compared. Adding the /i switch will make string comparisons case-insensitive (UPPER/lowercase).​ value1 EQU value2 True if the values are equal​ value1 NEQ value2 True if the values are not equal​ value1 LSS value2 True if the value1 less than value2 ​ value1 LEQ value2 True if the value1 less/equal to value2​ value1 GTR value2 True if the value1 less/equal to value2​ value1 GEQ value2 True if the value1 less/equal to value2​ To force a string comparison, enclose ''value1'' and ''value2'' in quotes. Otherwise, the shell will determine if the variables appear to contain integer values and compare them as integers, or otherwise compare them as strings. == Tests Group 4: Variable Definition, Errorlevel == Test to see if a variable is defined​: ​ DEFINED variable True if variable is defined​ Test to see if the ERRORLEVEL is above a threshold​: ERRORLEVEL value True if ERORRLEVEL>=value​ Although it's probably better to just an integer comparison such as: ''%ERRORLEVEL% GEQ value​'' ==== Notes about IF and these Tests ==== * These tests work only with the ''IF'' command * The IF command can be used with ''GOTO'' and a label: IF test GOTO :skip​ ...​ :skip​ Note that using a GOTO in a loop will make the shell forget about the loop, regardless of where the label is located!​ ==== Negating and Combining Tests ==== You can negate (invert) a test with the NOT operator: IF NOT EXIST %N% ECHO The file %N% does not exist. Note that you cannot combine tests - there is no ''AND'' or ''OR'' operator. ===== Script Parameters ====== Arguments to a script are called parameters. You can access the parameters using the special variables ''%0'', ''%1'', ''%2'', and so forth. ''%0'' contains the name of the script, ''%1'' contains the first parameter, ''%2'' contains the second parameter, and so forth. Note that there is no second percent sign after the parameter number. The shift command gets rid of the first parameter and shifts every parameter to a lower number.​ Examples: > type params.cmd​ @echo off​ ECHO PARAM 0: %0​ ECHO PARAM 1: %1​ ECHO PARAM 2: %2​ > params red green blue​ PARAM 0: params​ PARAM 1: red​ PARAM 2: green​ > type params-shift.cmd​ @echo off​ ECHO List of all arguments: %*​ :start​ IF "%1"=="" GOTO :done​ ECHO %1​ SHIFT​ GOTO :start​ :done​ > params-shift yellow orange red​ List of all arguments: yellow orange red​ yellow​ orange​ red The special variable ''%*'' returns __all__ of the parameters. On the command line, parameters may be separated by: * Space (or Tab) * Comma [,] * Semicolon [;] * Equal sign [=] ===== Looping ===== In the Windows CMD shell, looping is performed with the ''FOR'' statement. Loops are controlled by an iterator variable, which has special rules: * The iterator variable name must be a __single letter__ * The variable name is __case sensitive__ * The variable name is written with a single preceeding percent sign [%] at all times when used from the command line, or double preceeding percent signs [%%] inside a script ==== Delayed Expansion ==== When the body of a ''FOR'' loop is executed, the variables are expanded (replaced by their values) before the loop begins. That means that any variables that are contained in the loop have their values locked-in and they can't be changed while the loop is executing.​ To allow updated variable values to be accessed within a loop:​ 1, Set the EnableDelayedExpansion option:​ SETLOCAL EnableDelayedExpansion​ ​ 2. Change any variables which will be updated during the execution of the loop by replacing the percent-signs ( % ) with exclaimation-marks ( ! ):​ %ERRORLEVEL% -> !ERRORLEVEL!​ ==== Loop throush a List of Files or Parameters ==== To loop through a list of values such as filenames, use: FOR %variable IN (files) DO list​ The //variable// will be sequentially set to each of the given //files// values, and the loop body (//list//) will be executed once for each value. The //files// value may be: * A list of filenames: ''FOR %F IN (file1.txt file2.pdf file3.c) DO ECHO %F'' * A filename pattern: ''FOR %F IN (*.pdf) DO ECHO %F'' * Fixed strings: ''FOR %C IN (Red Green Blue) DO ECHO %C'' * Or a list of all of the parameters: ''FOR %P IN (%*) DO ECHO %P'' The ''/D'' option can be used along with a filename pattern to match only directories: FOR /D %variable IN (files) DO list === Example === @echo off​ SETLOCAL EnableDelayedExpansion​ FOR %%F IN (*) DO (​ rem The CHOICE command presents a Y/N choice and sets %ERRORLEVEL% rem to 1 if the user selected Y and 2 if the user selected N CHOICE /M "DELETE %%F"​ IF !ERRORLEVEL!==1 (​ ECHO ...Deleting %%F​ DEL %%F​ ) ELSE (​ ECHO ...Skipping %%F​ )​ ) ==== Loop through a Range of Integers ==== FOR /L (start, step, end) DO list This type of loop counts forward or backwards from ''start'' to //end// by a given //step//. Example:​ ​ @echo off​ rem Count from 0 to 5 in increments of 1​ FOR /L %%I IN (0, 1, 5) DO ECHO ... %%I ...​ rem Count from 4 to 0 in increments of -1​ FOR /L %%I IN (4, -1, 0) DO ECHO ... %%I ... Output: 0 1 2 3 4 5 <- This is the end of output from the first loop, having gone from 0 to 5 4 <- The second loop starts output here, going from 4 to 0 3 2 1 0 ===== Examples ===== === Examples of using test: Integers vs Strings​ === @echo off​ rem intcmp.cmd - compare as integers​ SET /A A=11,B=2​ IF %A% GTR %B% (​ ECHO %A% is greater than %B%​ ) ELSE ECHO %A% is less than or equal to %B% ​ @echo off​ rem strcmp.cmd - compare as strings​ SET /A A=11,B=2​ IF "%A%" GTR "%B%" (​ ECHO %A% is greater than %B%​ ) ELSE ECHO %A% is less than or equal to %B% === Examples of using test: Integer Numbers​ === @echo off​ SET /A COIN=%RANDOM% %% 2​ IF %COIN% EQU 0 (ECHO Heads!) ELSE ECHO Tails