test2
Index
Multi-line Scripts and Multi-Scripts
Scripting and User-Defined Commands
Step Mode: Stepping Through Scripts
Variables Scope and Lifetime: Local, Global, and Permanent Variables
Introduction
XYplorer Scripting, introduced with version 7.0, can truly be seen as the ultimate in file management efficiency. Roll your own custom commands, combine them to scripts, wrap them in an XYplorer Script file (XYS), or a User-Defined Command, and trigger them by just a click or a keystroke. You may share scripts with colleagues, or download them from the internet. Just drop a script file into your app folder and fresh plug-in commands are at your finger tips.
Without doubt, scripting is an advanced feature that only pays off if you take the time to dive into it and explore the ways and possibilities. However, you will eventually find out that it ain't rocket science at all.
More information, downloads, and a growing pool of examples is available here:
Warming Up
To get you started let's try something easy:
(1) Select menu Scripting | Run Script...
(2) Paste msg "Hello world!" into the edit box.
(3) Click OK.
You should see a "Hello world!" message box now. Well done, you just wrote your first XYplorer script!
Okay, now for something a little bit more interesting:
(1) | Try msg %temp%. You should see a message box that displays your TEMP path. %temp% is a standard Windows environment variable. |
(2) | Try msg "XYplorer.exe runs from <xypath>". <xypath> is a native XYplorer variable that resolves to XYplorer's application path. |
(3) | Try msg "Press OK to copy the time!"; copytext "<date hh:nn:ss>". When the message box is OKed, the second command copytext is immediately executed and the current time is copied to the clipboard. |
(4) | Try set $a, "<curpath>"; msg $a. You should see a message box displaying the current path. First the variable $a is set to the current path, then it is used in the msg command. |
(5) | Try $a = "Year " . "<date yyyy>"; msg $a. You should see a message box displaying "Year 2009" (or whatever the year might be when you try this). First the variable $a is set to two strings, one literal and one variable, concatenated by a dot, then it is used in the msg command. |
(1) | There are commands consisting of a function name like msg or copytext, and arguments that can contain literals like "Hello!", and variables like %temp% (environment), <curpath> (XYplorer), or $a (user-defined). |
(2) | An argument can have more than one part. The parts are concatenated by dots ( . ). |
(3) | A command can have more than one argument. The arguments are separated by commas ( , ). |
(4) | A script can have more than one command. The commands are separated by semi-colons ( ; ). |
(5) | Arguments are identified either by position, or by a prefixed number denoting their position (see Numbered Arguments below). |
Examples
This little script will rename all currently selected items by appending the current date:
rename b, "*-<datem yyyymmdd>"
This script goes to the Desktop folder (ensuring that the listing is unfiltered), sorts the list by date modified (descending), selects the first item, and moves the focus to the list:
goto "Desktop|"; sortby m, d; sel 1; focus "L";
Numbered Arguments
Instead of counting commas you now can prefix the position of the argument to the argument itself. The separator is ":=", the first position is 0 (zero).
For example, instead of this:
text popupmenu("A,B,C", , , , , , ",");
You can do this:
text popupmenu("A,B,C", 6:=",");
You can even do this (reversed or arbitrary order):
text popupmenu(6:=",", 0:="A,B,C");
Or this (processing is from left to right, the last mapping wins):
text popupmenu("a,b", 6:=";", 6:="|", 6:=",", 0:="A,B,C");
Concatenation
Strings and variables can be concatenated by dots. Any number of blanks surrounding the concatenator are ignored. The following scripts are identical:
msg "Hi there!"
msg "Hi "."there!"
msg "Hi " . "there!"
msg "Hi " . "there!"
msg "Hi"." "."there!"
It's strongly recommended that you (double- or single-) quote your strings! While the script engine is currently still permissive with unquoted strings (msg Hi! works) this might not be so in the future, so you better do msg "Hi!" right away!
Here's the basic laws of quoting:
(1) | Everything is either in double-quotes, or in single-quotes, or outside of any quotes. |
(2) | To have double-quotes inside double-quotes they must be doubled. |
(3) | To have single-quotes inside single-quotes they must be doubled. |
(4) | Variables are resolved in double-quotes and outside of any quotes, but not in single-quotes. |
There are two types of commands: commands (proper) and functions. Contrary to commands (e.g. echo), functions (e.g. quote()) return an in-place value.
In the first example, the quoting is achieved by doubled double-quotes. The only command in the statement is echo. In the second example, the quoting is achieved through the function quote():
echo """Hi!"""; //"Hi!"
echo quote("Hi!"); //"Hi!"
Remarks on Functions
(1) | Function names are not case-sensitive: QUOTE() = quote(). |
(2) | Even without any argument -- e.g. quote() -- the parentheses are mandatory, else a function is not recognized. |
(3) | You can call a function without caring for the return argument (but you need the parentheses!): renameitem("John", , , "-01"); Optionally you can use the dummy command call: call renameitem("John", , , "-01"); |
(4) | Functions are not resolved (interpolated, see below) when inside double-quotes. |
Command Prefixes
Now you can add a prefix to each command to modify its behavior. Currently one prefix is implemented:
e = skip the big error debugging dialog
The prefix is separated from the command by | (pipe). For example, when you run this command in the drives listing:
rename , '*-<datem yyyymmdd>'; //shows error dialog
e|rename , '*-<datem yyyymmdd>'; //skips error dialog
Quick Scripting means: Scripts, when prefixed with "::" (double-colon), can be executed directly (i.e. quickly) through any interface in XYplorer that can process locations: Address Bar, Go To, Favorites, Catalog etc. This gives you a lot of choices for usage and storage of scripts.
Let's try:
Paste ::msg "Hello world!" into the Address Bar and press Enter. You should see a "Hello world!" message box.
Now, for mouse users, the Catalog is a very good place for scripts. Using the "::" prefix, scripts can be simply entered into the Location field of a catalog item. They are executed on a single click.
For example:
(1) | Add a new category "Scripts" and add a new item to it. |
(2) | Set item's Location field to ::#1026 and save it. The icon turns into the script symbol. |
(3) | Now click the item. The Find Files tab on the Info Panel is opened, all search filters are reset, and the cursor is placed into the Name field. Nice! |
But how did this work? And what is #1026? Read on...
Note: From version 9.70 onwards the "::" prefix is not necessary anymore when you end your script line with a trailing ";" (appended comments after the ";" are allowed). This behavior is enabled by the INI setting ScriptSmartDetect=1.
In XYplorer almost every function has a fixed number, the function ID, by which it can be referred to in a script. ID #1026 happens to refer to "Miscellaneous / Find Files / Open Find Files and Reset". Open the Customize Keyboard Shortcuts dialog (menu Tools) and find this function in category Miscellaneous. At the bottom of the dialog you'll see a button showing the function's ID. Clicking this button will copy the function's ID to the clipboard, making it easy to use it in a script.
You can execute almost any function in XY in a script by referring to its function ID. E.g. #230 will pop up the submenu "New" of menu Edit.
Step Mode: Stepping Through Scripts
Suppose you have an older script #230, and you forgot what #230 refers to. Or you downloaded a script from the internet, and don't know exactly what it does. What now? Very simple, enter Step Mode:
(1) | Open the Scripting menu and select Step Mode. |
(2) | Now execute your mysterious script. |
A small window (resizable), the Step Dialog, will pop up and tell you what is about to happen. You can now decide whether to execute the command, or skip it, or cancel the whole script. In stepping mode you are on the safe side. It is highly recommended when writing or debugging scripts!
There's also a toolbar button for toggling the stepping mode. When pressed (stepping is ON) its color changes to red to make the current state very clear.
Error Messaging and Risk Classes
The Step Dialog also tells you what went wrong where, and it informs you about the potential risk associated with the command to be executed. There are the following risk classes:
#3 | Potentially harmful because the function ID might refer to some risky process which is unknown at this point (either an internal command or a User-Defined Command): #[function ID] |
#2 | Potentially harmful because files or folders are affected: backupto, copyto, delete, download, moveto, new, open, openwith, rename, rotate, run, setkey, swapnames, timestamp |
#1 | Potentially harmful because Step Mode is terminated: unstep |
#0 | Harmless: (all others) |
Class #3, #2, and #1 are marked with a yellow "warning" icon, class #0 with a blue marble (think "cool") icon.
Script Context
The first two lines in the Step Dialog display (1) the current script resource, and (2) the current script caption. Valuable information when writing and debugging scripts.
How Functions are Stepped
Functions are individually stepped. The current script line is marked green when a contained function is stepped as opposed to the main command of the line. You can even skip (button Skip) functions individually, in which case the function name is returned with the resolved arguments. For example:
msg quote(chr(64));
· | If you continue both functions the result is: "@" |
· | If you skip chr() and continue quote() the result is: "chr(64)" |
· | If you continue chr() and skip quote() the result is: quote(@) |
· | If you skip both functions the result is: quote(chr(64)) |
When you "Continue without Stepping" on a function, the stepping is only suspended for all functions in the current script line / command.
Variables... (Button)
Click the Variables... button to display all currently assigned variables with their current values. Within the new "Variables on Stack" dialog you can double-click any listed variable to show its current value.
Right-click that button for further options:
Show User Functions will list all currently declared user functions. Within the new "User Functions" dialog you can double-click any listed function to show its code.
Scripting and User-Defined Commands
Now, for passionate keyboarders, there are User-Defined Commands (UDCs). Using the "::" prefix, scripts can be simply entered into the Location field of a UDC Go To item, which then can be assigned a keyboard shortcut to.
For example:
(1) | Click menu User | Manage Commands... |
(2) | Select the category Go To, and click the New button. |
(3) | Enter ::text "<clipboard>" into the Location field. |
(4) | Assign a keyboard shortcut, say Ctrl+Alt+3. |
(5) | Click OK. |
Now press Ctrl+Alt+3. You should see a small window displaying the current textual contents of the clipboard.
Of course, you don't have to abuse the UDC Go To for scripts. There's also the UDC Run Script which accepts scripts without a prefixed "::", e.g. text "<clipboard>".
Run Script can also handle more advanced stuff like multi-line scripts, and multi-scripts, the rules and possibilities of which will be further explained below under "XYplorer Script Files".
Multi-line Scripts and Multi-Scripts
A script can have more than one line (multi-line script), and a script resource can have more than one script (multi-script). There is one important formatting rule for multi-line scripts:
In a multi-line script all lines apart from the first line have to be indented by at least one space.
Each non-indented line is interpreted as the first line of a separate script, and if there are more than one scripts in a loaded script resource (i.e. a multi-script) then a menu is popped where you can select the script to run.
For example, this is a multi-script containing two multi-line scripts. In the first script, the script caption (which is used as the menu caption) forms the first line:
"Go to C:\"
goto "C:\";
goto
"%winsysdir%";
selectitems
"calc.exe";
Comments are an exception: Any number of non-indented comments can be prefixed to a multi-line script. Actually non-indented comments can be inserted anywhere. This script is functionally identical to the above one:
// comment 1
// comment 2
"Go to
C:\"
// comment 3
goto "C:\";
// comment
4
goto "%winsysdir%";
selectitems "calc.exe";
// comment 5
Initialize and Terminate
You can define two special-named scripts within multi-scripts, "_Initialize" and "_Terminate". They can be placed anywhere in the multi-script, and they are not shown in the popup menu. The script called "_Initialize" will be auto-processed before the menu is popped. The script called "_Terminate" will be auto-processed after the script selected from the menu has been processed (or after the menu has been canceled).
Note: If a script within a multi-script is called directly (SCs Sub or Load), then "_Initialize"/"_Terminate" are NOT called.
For example, this multi-script defines a permanent variable (which is also global by definition), then offers various scripts in a popup menu, then finally removes the variable from memory:
"_Initialize"
// if variable already exists it is NOT reset here but
keeps it current value
perm
$p_a;
// explicitly initialize it to
zero
$p_a = 0;
"_Terminate"
unset
$p_a;
"Show Variable"
echo
$p_a;
"Plus One"
$p_a = $p_a + 1;
echo
$p_a;
"Minus One"
$p_a = $p_a - 1;
echo
$p_a;
"Load Plus One"
// will NOT call "_Initialize"/"_Terminate"
Load "*", "Plus One";
// WILL call "_Initialize"/"_Terminate"
Load "*", "*";
"Sub
Plus One"
// will NOT call
"_Initialize"/"_Terminate"
Sub "Plus
One";
Here we are talking popup menus that are user-defined by a text file. Let's make one:
Create a new text file in any editor.
Paste the following multi-line script (see above):
// some little test
scripts
"Go to C:\"
goto "C:\"
"Go to
System Folder"
goto
"%winsysdir%"
"Go to XYplorer
Folder"
goto "<xypath>"
Save the file as "test.xys" (XYplorer Script File) in XYplorer's Scripts folder. In case you don't know that path, use menu Scripting | Go to Scripts Folder to go there.
Now, in XYplorer, click menu Scripting | Load Script Files... and open "test.xys". The following menu should pop up at your mouse cursor:
Go to C:\
Go to System Folder
Go to XYplorer Folder
Now you can choose where you want to go.
A script file is basically a library of scripts. It is nothing more than a simple text file, which can contain one or more scripts. You will be able to either call one of those scripts directly, or simply load the entire file. In such case, XY will create a menu based on the contained scripts in that file and pop it up, allowing you to choose which script to execute.
The syntax for the scripts themselves within script files is exactly the same as for scripts anywhere else in XY since this is just another way to store and execute scripts.
Syntax rules for XYplorer Script Files
(1) | Lines starting with // are ignored and can be used for comments. |
(2) | One script can run over multiple lines. Simply indent the lines after the first line using any number of space or tab characters. |
(3) | You can have more than one script inside a script file. In that case, loading the script file will pop up a menu presenting all scripts inside the script file by their captions. |
(4) | To set a script caption simply prefix the desired caption to the script, and wrap it in quotes. |
(5) | Within a caption you may define a label that allows you to execute a script from a file directly. |
(6) | Using the command sub in a script file you can execute other scripts inside the same file. |
(7) | You may hide scripts by prefixing an underscore (_) to their caption. |
To Load a Script File do one of the following:
(1) | Use menu Scripting | Load Script File... |
(2) | Use a User-Defined Command from category "Load Script File". |
(3) | Use the script command load [scriptfile]. |
The existence of a command load, of course, means that one script file can load another. You will get an idea of the potential of scripting by now...
You can drop files onto XYS-files. The dropped files are referred to in the dropped-on script by <get drop>.
This can get pretty cool in Dual Pane mode. You can have a folder with assorted script files in one pane, and your working folder in the other pane. Now you just drag-and-drop files onto the scripts for automated processing.
Primitive example script in a file "drop_on_me.xys":
text <get drop>;
Even cooler: You can also drop on multi-scripts. In that case you get the usual popup menu of choices, and the <get drop> will be available in each of the scripts.
Primitive example script in file "drop_on_me_2.xys":
"Text"
text <get drop>;
"Echo"
echo <get
drop>;
Notes:
· | If more than one file is dropped <get drop> returns
one per line. Alternatively you can pass a separator like this: text <get drop |>; |
· | The <get drop> variable is cleared after the script is processed, so it cannot be used after the drop event is completed. |
· | <drop> is a synonym for <get drop>. |
· | If the script contains no <get drop> variable it is run nevertheless just as if you loaded the script file. |
· | You can as well drop files on shortcuts (LNK) to XYS-files. |
· | You can also drop text onto XYS-files (and LNKs to XYS-files), e.g. selected text from a webpage. Just like with dropped files, the dropped text can be referred to in the dropped-on script by <get drop>. |
Labels
By using labels you can execute a script inside a file directly, avoiding the popup menu. The label is attached to the caption, separated by " : " (space-colon-space). For example:
// some little test scripts,
using labels
"Go to C:\ :
croot"
goto "C:\"
"Go to System Folder : system"
goto "%winsysdir%"
"Go
to XYplorer Folder : xy"
goto
"<xypath>"
If the above is saved to a file called "test.xys" in application data path then the following command will directly bring you to the System folder: load "test.xys", "system".
You may as well specify a list of labels, by which you can easily control which scripts are displayed in the popup menu, and in which order. See load for the details.
Wildcard catch-all label: The label "*" matches all load calls if more than one label is stated. The third command will be shown on load "test.xys", "croot;system":
// some little test scripts,
using labels
"Go to C:\ :
croot"
goto "C:\"
"Go to System Folder : system"
goto "%winsysdir%"
"Go
to XYplorer Folder : *"
goto
"<xypath>"
Hiding scripts inside a script file
Hidden scripts can be executed but are not shown in the script file's popup menu. To hide a script simply prefix an underscore to the caption or label (a hidden script does not need a caption anyway). For example, create a script file "date.xys" in application data path with the following contents:
// this is in script file
"date.xys"
"_date"
msg "<date yyyy-mm-dd>"
"_time"
msg "<date
hh:nn:ss>"
"Show Date :
date"
sub "_date";
"Show Date && Time : datetime"
sub "_date";
sub
"_time"
Now execute the script load "date.xys". The popup menu will show only two of the four contained scripts. Select either and see what happens.
Now run the script load "date.xys", "date". The script with the label "date" will be executed directly. It has only one command: sub "_date";. The sub command is a special command to call a script inside the same script file. In this case the hidden script with the label "_date" is called and executed. Its command msg "<date yyyy-mm-dd>" produces the message box showing the current date.
Variables in Captions
The captions of scripts in multi-script resources may contain XYplorer native variables. They are resolved in the moment the menu is popped up. For example, paste this into the Try Script box:
"Go to
<xypath>"
goto
"<xypath>";
"Is it <date
hh:nn:ss>?"
msg "It's <date
hh:nn:ss>!";
The 2nd script will show two different times if you wait longer than a second before you click the menu item.
Also Environment Variables and Permanent Variables are supported in captions.
Icons, States, and Levels
You can optionally define an icon and a state for each menu item.
Syntax:
"Caption|Icon|State|Level : Label"
Script
where
State
Default = 1
State Checked = 2
State Disabled = 4
Examples for States
"Go C:|C:|1" goto "C:\";
//shown bold
"Go D:|D:|2" goto "D:\";
//shown checked
"Go E:|E:|3" goto "E:\";
//shown bold and checked
Icon can be any file or folder, and its small system icon (the one you see in Details View) will be used for the menu, or a PNG, JPG, GIF, BMP, or TIF file. XY variables and environment variables are supported. The path defaults to the XY icon path <xyicons>.
You can as well use XYplorer's internal toolbar icons using the button key, e.g.:
"Funny Script|Icons\fun.ico"
echo "Ha!";
"Say Ho!|iexplore / 12" echo
"Ho!";
"Recent Locations|:mru" button
"mru"
"Hotlist|:hotlist" button
"hotlist"
Note that the icon specification in a multi-script resource supports permanent global variables.
You can as well use generic file icons in the multi-script menus by passing the generic extension as "*.ext":
"Megan|*.png" echo
'hello';
"Betty|*.jpg" echo
'hello';
You can as well pass * as placeholder to use the caption as icon pointer:
"C:\|*" goto
"C:\";
"C:\Windows|*" goto
"C:\Windows";
Levels: Multi-Scripts support nesting. The level is simply stated by the number denoting its depth, first level is 0 (zero), which is also the default, of course. Up to 256 levels are possible. For example:
"C:|*"
"Go to C:\|||1"
goto
"C:\";
"|||1" goto
"%winsysdir%";
selectitems
"calc.exe";
"D:|*"
"Go to D:\|||1"
goto
"D:\";
Relative Levels: Multi-script nesting can be defined with relative levels. This can be useful when building menus from local resources by use of the Include statement.
Syntax: A relative level is marked by a prefixed "+" character. The number following "+" is added to the last defined absolute level.
For example, the following two code samples create identical nested multi-scripts:
"A" echo "A";
"B|||1" echo "B";
"C|||2" echo "C";
"D|||1" echo "D";
"B|||2" echo "B";
"C|||3" echo "C";
"A" echo "A";
"B|||+1" echo "B";
"C|||+2" echo "C";
"D|||1" echo "D";
"B|||+1" echo "B";
"C|||+2" echo "C";
However, the second sample uses relative levels which allows it to re-use the same part of code twice:
"B|||+1" echo
"B";
"C|||+2" echo "C";
So this part of code could be outsourced to a file say "IncludedMenuBC.xys" and then be included like this:
"A" echo "A";
include "IncludedMenuBC.xys"
"D|||1" echo "D";
include "IncludedMenuBC.xys"
The Goto-Shorthand
In a multi-script resource you can pass a plain path/file spec as a shorthand for a well-formed goto script. Such a line will be auto-converted to a valid goto command including the appropriate icon in the generated popup menu.
Long version using well-formed goto scripts with captions:
"C:\|C:\" goto
"C:\";
"C:\Windows|C:\Windows" goto
"C:\Windows";
"C:\WINDOWS\SoundMan.exe|*"
goto "C:\WINDOWS\SoundMan.exe";
Equivalent shorthand version:
C:\
C:\Windows
C:\WINDOWS\SoundMan.exe
The goto-shorthand additionally supports environment and native variables, Quick Searches, and visual filters. For example:
Desktop
%tmp%
<xydata>
C:\WINDOWS\system.ini
C:\WINDOWS\system32
C:\WINDOWS\system32?a*
C:\WINDOWS\system32?:a* or b*
C:\WINDOWS\system32?:a* | b*
C:\WINDOWS\system32?lbl:blue
C:\WINDOWS\system32|a*
Two types of comments are supported, line end comments, and block comments.
Line End Comments
Line end comments begin with a double-forward slash (outside of any quotes) and end at
the end of the line:
$a = "<xypath>"; assert $a=="<xypath>"; //should not fail
$a = "<xypath>";
//assign XY path
assert
$a=="<xypath>"; //should not fail
"get CountSelected" //
comment
$count = get("CountSelected"); //
comment
assert $count!=0, //
comment
"You must select a file before
running this script!"
Block Comments
Block comments (aka C-style comments) start with /* (outside of any quotes) and end with the next */ (outside of any quotes) and can span any number of lines:
/* This is
a multi-line
block
comment
*/ msg "hi!";
msg /*they can be inserted anywhere!*/ "hi!" /* anytime */;
Remarks on Comments
(1) Line-end and block comments overwrite each other:
msg "Hi!"; // /*this is not a
block comment starting...
msg /* //this is
not a line end comment starting... */ "Hi!";
(2) Make sure you don't nest block comments. It is easy to make
this mistake if you are trying to comment out a large block of
code:
/*
msg 'This is a test'; /* Will cause a problem
*/
*/
This, however, will work since // overwrites the /* */ comment:
//msg 'This is a test'; /* This comment is NO problem */
Advancing in script writing, you will soon feel the need for variables. XYplorer allows you to define and use as many variables as you want, using a number of commands like set, input, replace, etc. The script set $a, "Hi!"; msg $a; will define a new variable $a and assign the string "Hi!" (without the quotes) to it; then a message box will display "Hi!". The same can be achieved using the assignment operator (=):
$a = "Hi!"; msg $a;
Interpolation
Variables are resolved wherever they are found in the arguments of all subsequent commands of the script, even if they are found inside double-quoted strings (see below, Interpolation); for example:
$name = "Ted"; msg "Hi, I'm uncle $name!";
will display the message "Hi, I'm uncle Ted!".
Format and Scope
Variables have to conform to the following rules:
(1) | Variables are represented by a dollar sign ($) followed by the name of the variable. |
(2) | Names start with a letter or underscore, followed by any number of letters, numbers, or underscores. |
(3) | Letters are a-z and A-Z. |
Good variables: $a, $ABC, $good_variable, $a1, $a_, $_a, $_
Bad variables: a, ABC, $1, a$, $ä, $., $
Variable names are case-sensitive.
The scope and lifetime of a variable begins and ends with the script where it has been defined.
Using the equal-operator (=)
To assign a value to a variable you can simply use the following syntax (as an alternative for the set command):
$a = "b"; or $a="b"; (spaces are ignored)
For the additional "reprocess" operand see description of the set command.
Increment Syntax (++/--)
The common increment syntax using the ++ (--) operator is supported.
$i=5; $i++; msg $i; //6
$i=5; $i--; msg $i; //4
$i++; msg $i; //1
You can also use $i++/$i-- as an argument. The value is incremented/decremented after it is passed to the function*:
$i = 1; echo $i++; echo $i; //1; 2
$i = 1; echo 1 + $i++; echo $i; //2; 2
$i = 1; echo $i++ + 1; echo $i; //2; 2
$i = 1; echo $i++ . $i++ . $i; //123
Note in the following that the left operand is incremented before the right operand is added. Finally, after the addition, the right operand is incremented:
$i = 1; echo $i++ + $i++; echo $i; //3; 3
* Note that before v14.30.0100 the value was incremented/decremented before it was passed to the function!
Interpolation
Interpolation means that variables that are embedded in double-quoted or unquoted strings are resolved (replaced with their corresponding value). Examples:
$name = "Ted"; msg "Hi, I'm uncle " . $name . "!";
Displays "Hi, I'm uncle Ted!". The variable is concatenated with literal strings.
$name = "Ted"; msg "Hi, I'm uncle $name!";
Displays "Hi, I'm uncle Ted". The variable is interpolated inside double-quoted literal strings.
$name = "Ted"; msg "Hi, I'm " . uncle $name!;
Displays "Hi, I'm uncle Ted". The variable is interpolated inside non-quoted literal string uncle $name!. Note that using non-quoted literal strings is not recommended and might even be deprecated in a future version!
You can block interpolation by using single-quotes:
$name = "Ted"; msg 'Hi, I''m uncle $name!';
Displays "Hi, I'm uncle $name!". The variable is not interpolated inside single-quoted literal strings. Note that single-quotes embedded in single-quotes have to be doubled (I''m).
msg '%TMP% = ' . %TMP%;
Displays "%TMP% = C:\Temp". The first %TMP% is blocked from interpolation by being embedded in single-quotes.
$date = "<date>"; msg '$date = ' . $date;
Displays "$date = 28.08.2008 12:23:12".
Variables Scope and Lifetime: Local, Global, and Permanent Variables
Local Variables
By default, all variables in XY scripting are local, i.e. they are not shared between called and calling scripts. In other words, whenever one script calls another script (e.g. using commands "sub" or "load"), a new local namespace is created by the called script and pushed on the stack.
Global Variables
Generally, global variables are shared between scripts. This can make scripts hard to maintain. However, the mechanism of "globalization" -- (almost) identical to the one used in PHP -- used by XY scripting gives you maximum control over what is shared and where. Global variables are implemented by means of the command global.
Permanent Variables
The lifetime of local and global variables ends with the current script or script stack. Permanent variables, however, stay alive in memory for the whole XYplorer session, or even across sessions if configured like this (Configuration | Refresh, Icons, History | Remember permanent variables). Hence permanent variables can be easily shared between scripts. Permanent variables are implemented by means of the command perm.
Permanent Variables created / modified in a called script are immediately visible / updated in the calling script when the called script returns.
Note the scripting commands writepv and readpv by which you can read and write permanent variables from/to file. This allows for some interesting use as portable data storage.
Permanent variables can be accessed from anywhere in the app using the <perm ...> meta variable. See Variables.
To release all permanent variables from memory use the command releaseglobals. You can as well unset them individually via menu Scripting | Permanent Variables directly from the right-click menu in the variable list.
To view and modify the current permanent variables use menu Scripting | Permanent Variables.
Initial Values
Local variables that have never been set to a value return their name as value (strictly speaking they aren't variables but normal strings). Global and permanent variables that have never been set to a value are initialized to "" when they are declared by global or perm:
msg $a; // displays "$a" if $a is has not set to any value before, and has not been declared as global or permanent.
global $b; // declare global variable
msg $b; // displays "" if $b has not been set to any other value before.
perm $c; // declare permanent variable
msg $c; // displays "" if $c has not been set to any other value before.
Two types of arrays are supported, indexed arrays and associative arrays. The syntax is similar to that used in eg C++, PHP or Java.
Here are some examples for indexed arrays:
$a[0]="pussy"; echo $a[0]; //pussy
$a[0]="pussy"; $a[1]="cat"; echo $a[0].$a[1]; //pussycat
$a[2]="cat"; $b=1; echo $a[$b+$b]; //cat
Array elements also work within quotes:
$a[0] ="cat"; echo "It is a $a[0]!"; //It is a cat!
$a[0]="pussy"; $a[1]="cat"; echo "$a[0]$a[1]"; //pussycat
Here are some examples for associative arrays (the named keys can be single- or double-quoted):
$a['pussy']="cat"; echo $a["pussy"]; //cat
$a["pussy"]="cat"; $b="pussy"; echo $a[$b]; //cat
If the index or key is invalid the variable is seen just as a bit of text:
$a[0] ="cat"; echo "It is a $a[1]!"; //It is a $a[1]!
$a["pussy"] ="cat"; echo "It is a $a['fussy']!"; //It is a $a['fussy']!
Also a missing key makes the variable invalid:
$a[0] ="cat"; echo "It is a $a[]!"; //It is a $a[]!
If you assign a non-first element in a new or smaller indexed array, all previous elements starting with [0] are automatically created (with value ""):
$a[1] ="cat"; echo $a[0]; //"" ($a[0] is implicitly created and set to "")
$a[0] ="cat"; echo $a[1]; //$a[1] ($a[1] does not exist as variable)
The index can be a complex expression:
$n = 4; $a[$n+4] = $n * 4; echo $a[$n+4]; //16
$b[0] = "pus"; $b[1] = "sy"; $a[$b[0] . $b[1]] = "cat"; echo $a[$b[0] . $b[1]]; //cat
You can copy one array to the other (appending [] to the variable name is optional):
$b[] = $a[];
$b = $a; //$a is an array variable; $b becomes an array variable
Some more properties of arrays:
· | The global command is supported by arrays, eg: global $a[]; |
· | The perm command is not supported by arrays but is simply ignored: Arrays cannot be permanent. |
· | Allowed range of elements in an indexed array: 0 to 32767. |
· | Maximum number of elements in and associative array: 32768. |
Special Function array()
There is a special function array() to populate arrays. Non-existing arrays are created, dimensioned and populated, existing arrays are redimensioned and overwritten.
$a = array("cat", "dog"); echo $a[0]; $a = array("dog"); echo $a[0]; //cat, dog
Notes:
· | You would usually pass literal strings as values, not variables. Values are separated by commas. |
· | You can also pass a single variable to the array()
special function that resolves to a list of strings. For
example: $values = "vampire, cow"; $arr[] = array($values); echo $arr[0]; //vampire |
· | The values can be in double quotes (which will be
removed), or also without quotes (fine if you are not using any commas or
flanking spaces within the values): $a = array(cat, dog); echo $a[0]; //cat |
· | The values can also be in single quotes but those will not be removed. |
· | If you like you can append [] to the variable, it makes
no difference: $a[] = array("cat", "dog"); echo $a[0]; //cat |
· | The values are added to the array in the order they are listed, starting with element [0]. |
· | So far the indexed arrays, but you can also populate
associative arrays using array(): $name = array("cat" => "pussy", "dog" => "rex"); echo $name["cat"]; //pussy General syntax: ... = array("key1" => "value1", "key2" => "value2") Again, you can get away with stripping the quotes and the spaces: $name = array(cat=>pussy,dog=>rex); echo $name["dog"]; //rex |
· | You can use array() without any values to completely
reset an array: $a = array("cat", "dog"); $a = array(); echo $a[0]; //$a[0] |
· | After "$a = array();" the variable $a is an array with
zero elements: $a = array("cat", "dog"); $a = array(); echo count($a); //0 |
The syntax here is a bit different from the Foreach Loops with Lists syntax.
General form:
foreach($array as $value,
[flags]) {
statement(s) using
$value;
}
Example:
$a = array("cat", "dog",
"bat");
foreach($a as $value)
{
echo $value;
}
Reversing the order (r flag) is supported:
$a = array("cat", "dog",
"bat");
foreach($a as $value, "r")
{
echo $value;
}
Skipping empty items (e flag) is also supported:
$a = array("cat", "",
"bat");
foreach($a as $value, "re")
{
echo $value;
}
The Foreach Loop also supports returning the keys in associative arrays. General form:
foreach($array as $key =>
$value, [flags]) {
statement(s) using $key
and $value;
}
Example:
// make associative
array
$freelancer = array(
"name" => "Groot",
"email" => "groot@gmail.com",
"age" => 22,
"gender" => "unknown"
);
// loop through
array
foreach($freelancer as $key =>
$value) {
echo "$key:
$value";
}
Reversing the order (r flag) and skipping empty items (e flag) is supported.
The key variable is set to the numeric index if you do 'foreach($array as $key => $value)' on an non-associative array:
$key="FOO"; $a = array("cat",
"dog", "bat");
foreach($a as $key =>
$value) { //always overwrites $key
$b[$key] = "$key=$value";
}
text implode($b);
//0=cat|1=dog|2=bat
You can nest expressions using parentheses, aka round brackets: ( ). There's no practical limit to nesting depth and superfluous parentheses are silently removed.
Examples where parentheses are merely decor:
msg "a" . "b";
msg ("a" . "b");
msg ("a") . ("b");
msg (("a") . ("b"));
msg ((("a") . ("b")));
msg "a" . "b" . (); //"ab"
msg "a" . ("b" . "c");
msg ("a" . "b") . "c"; //"abc"
msg "a" == "a" . "a" == "b";
msg ("a" == "a") . ("a" == "b"); //"10"
Examples where parentheses actually make a difference:
msg "a" == "a" . "a";
msg ("a" == "a") . "a"; //"1a"
msg "a" == ("a" . "a"); //"0"
Examples for nesting errors:
msg ("a" . "b"; // ')' missing! -> ("a" . "b"
msg "a" . "b"); // '(' missing! -> = a"b")
Scripting can do basic calculation using math operators +-*/, and also \ (integer division), % (modulo), and ^ (exponentiation). Fractions and parentheses are supported. Also unary operators + and - are supported.
Examples
echo 1 + 1;
echo 1 - 2 - 3; // -4
echo 1 - (2 - 3); // 2
echo 1/3 + 1/3;
echo 1 + 2 * 3;
echo (1 + 2) * 3;
echo 1/0; // ERROR: division by zero
$a=3; $b=2; echo $a / $b; // 1.5
$fraction = 1.5; echo $fraction == 3/2; // true
echo 1.2 + 2.1; // 3.3
echo 1 + --1; //2
echo --(1 * ----2); //2
echo 5 \ 2; //2 (integer division)
echo 5 / 2; //2.5
echo 5 % 2; //1 (modulo)
echo 2 ^ 3; //8
echo 4 ^ 0.5; //2
echo 4 ^ -1; //0.25
Remarks
(1) | Strings are converted to numbers as possible (just
like in comparisons). $a=""; $b="2"; echo $a + $b; // = 2 $a="1a"; $b="2b"; echo $a + $b; // = 3 |
(2) | The decimal separator is NOT locale specific (e.g. dot in US, comma in Germany) but hard-coded to dot (period). This way scripts are interchangeable between regions. |
(3) | Calculation uses 8-byte floating point numbers with
the following ranges: negative: -1.79769313486232E308 to -4.94065645841247E-324 positive: 4.94065645841247E-324 to 1.79769313486232E308 Floating point arithmetic has its natural shortcomings so don't expect too much in terms of precise accuracy down to the 12th digit. A file manager is not a scientific calculator. |
(4) | The operator precedence is
^ > (*,/) > (\,%) > (+,-) meaning that * and /, \ and %, + and - are of equal
weight. Processing of math terms is from left to right: echo 36 / 4 * 3; // 27! (not 3) |
(5) | With integer division (the
portion of the result after the decimal point is lost) and modulo
(the remainder of integer division) the operands are rounded before
the operation! echo 5.9 \ 2.1; //3! echo 7.9 % 2.5; //2! (8 % 3 = 2) echo 7.9 \ 0.4; //ERROR: Division by zero. echo 7.9 % 0.4; //ERROR: Division by zero. |
(6) | Fractional
numbers should be stated with a dot as
decimal separator, or alternatively in quotes with the local decimal
separator: echo 7.5 / 2.5; //3 -- works under each locale echo "7,5" / "2,5"; //3 -- works only where comma is the decimal separator Internally decimal separator are stored and processed according to the system locale: $n = 2.5; echo $n; //$n is "2,5" internally where comma is the decimal separator |
The parser recognizes the common prefix 0x (zero-x) for hexadecimal values. Supported are up to 4 bytes per number, i.e. 8 hex digits. The hex digits are case-insensitive (the prefix is not).
Here for some examples; hex values are automatically resolved to decimal values:
echo 0xa; //10
echo 0xA; //10
echo 0xFFFFFF; //16777215
echo 0xFFFFFFFF; //-1
echo 0x7FFFFFFF; // 2147483647
echo 0x80000000; //-2147483648
echo 0xA + 0xA; //20
The following are examples for invalid hex strings; they are returned unresolved:
echo 0xG; //0xG (invalid value)
echo 0x; //0x (no value)
echo 0XA; //0XA (wrong prefix)
echo 0x123456789; //0x123456789 (too many characters)
echo "0x12345678"; //0x12345678 (quoted)
Tip: To convert HTML #RRGGBB colors to color decimals use this formula:
0xBBGGRR
The parser recognizes the common prefix 0b (zero-b) for binary values. Supported are up to 4 bytes per number, i.e. 32 binary digits. Here for some examples:
echo 0b0; //0
echo 0b1; //1
echo 0b11; //3
echo 0b10000000; //128
echo 0b11111111; //255
echo 0b100000000; //256
echo 0b00000000000000000000000000000001; //1
echo 0b10000000000000000000000000000000; //-2147483648
echo 0b11111111111111111111111111111111; //-1
The following are examples for invalid binary strings; they are returned unresolved:
echo 0b2; //0b2 (invalid value)
echo 0b; //0b (no value)
echo 0B1; //0B1 (wrong prefix)
echo 0b110000000000000000000000000000000; //0b110000000000000000000000000000000 (too many characters)
echo "0b1"; //0b1 (quoted)
Comparisons of two values are evaluated in-place. The "comparison" has the general form
"a" operator "b"
where "a" and "b" are string expressions, and "operator" can be one of the following:
== Equal
!= Not Equal
< Less than
> Greater than
<= Less than or Equal to
>= Greater than or Equal to
Like Matches Pattern (case-sensitive)
LikeI Matches Pattern (case-insensitive)
UnLike Does not match Pattern (case-sensitive)
UnLikeI Does not match Pattern (case-insensitive)
If the comparison evaluates as True it is set to "1", else it is set to "0".
Examples
msg "a" == "a";
= shows '1'
msg ("a" == "a") . ("a" == "b");
= shows '10'
$r = ("a" == "a"); $r = ($r?"True":"False"); msg $r;
= shows 'True'
$comparison = "a == a"; assert $comparison == "a == a";
= no assertion error
$minver = "7.60.0009"; assert "<xyver>" >= $minver, "This script needs at least XY $minver!"
= no assertion error if XY version is >= 7.60.0009
Strings and Numbers
If you compare two numerical strings, they are compared as integers:
msg ("2" < "10"); // -> true! (both parts are numeric)
msg ("2" < "10a"); // -> false! (only one parts numeric)
msg ("2a" < "10a"); // -> false! (both parts are non-numeric)
The Like (LikeI) and UnLike (UnLikeI) operators
General form:
String Like Pattern (case-sensitive)
String LikeI Pattern (case-insensitive)
Where string is any string expression and Pattern may contain the usual wildcards and special chars used in XY patterns (*?#[]). Note that exact capitalization matters: "Unlike" or "unlike" won't work. These operators must be surrounded by spaces.
Examples:
echo "abc" Like "a*"; //1
echo "Abc" Like "a*"; //0!
echo "Abc" LikeI "a*"; //1!
echo "abc" UnLike "a*"; //0
echo "Abc" UnLike "a*"; //1!
echo "Abc" UnLikeI "a*"; //0!
echo "Abc" LikeI "abC"; //1 (no wildcard!)
echo "It's " . ("<date yyyy>" Like "20??"?:"not ") ."the 21st century";
Scripting supports Boolean operators in the following order of precedence (see also Operator Precedence):
! NOT (unary operator)
&& AND
|| OR
and AND (case-insensitive: AND, And...)
xor XOR (case-insensitive: xOR, Xor, XOR...)
or OR (case-insensitive: OR, Or...)
Examples
echo not 1; // 0
echo not 0; // 1
echo ! 1; // 0
echo !1; // 0
echo !!!1; // 0
echo !(1 and 0); // 1
// parsed as: (TRUE and FALSE)
or (TRUE and TRUE);
echo TRUE and FALSE or
TRUE and TRUE; //1
// will show "1", then
"done"
$i = 1;
while ($i < 2 && $i > 0) {
echo $i;
$i++;
}
echo "done";
The Boolean constants TRUE and FALSE (case is ignored) are recognized if they are used unquoted.
TRUE is resolved to 1.
FALSE is resolved to 0.
Examples
echo TRUE and TRUE; // 1
echo TRUE and FALSE; // 0
echo TRUE and false; // 0 (constants are case-insensitive)
echo TRUE and "false"; // 1! (quoted "false" is NOT a constant)
echo (1==1) == TRUE; // 1
echo (0==1) == FALSE; // 1
echo (1==1) != FALSE; // 1
Note that in a Boolean context the values "" and "0" evaluate to 0 (FALSE). All other values evaluates to 1 (TRUE):
echo "dog" and TRUE; // 1 (TRUE and TRUE)(Boolean context)
echo "dog" == TRUE; // 0 ("dog" == "1") (no Boolean context)
echo "0" == FALSE; // 1 ("0" == "0")(same strings)
echo "" == FALSE; // 0 ("" == "0") (no Boolean context)
echo "" XOR TRUE; // 1 (FALSE XOR TRUE)
echo "0" XOR TRUE; // 1 (FALSE XOR TRUE)
echo "dog" XOR TRUE; // 0 (TRUE XOR TRUE)
Note that "0" and 0 ("1" and 1) are the same in XY scripting, so:
echo 0 == FALSE; // 1 (0 == 0)
echo 0 XOR TRUE; // 1 (FALSE XOR TRUE)
Scripting knows so-called "ternary conditionals" as used in many other programming languages. The logic is this:
if (condition) {
variable = value-if-true;
} else {
variable = value-if-false;
}
As ternary conditional the same can be written like follows:
variable = (condition) ? value-if-true : value-if-false;
The parentheses and the blanks are optional, so these are identical:
variable = (condition) ? value-if-true : value-if-false;
variable = (condition)? value-if-true: value-if-false;
variable = condition?value-if-true:value-if-false;
The part "condition" has the form
"a" operator "b"
where "a" and "b" are string expressions and "operator" can be one of the following:
== Equal
!= Not Equal
< Less than
> Greater than
<= Less than or Equal to
>= Greater than or Equal to
(none) True if expression is not 0 and not ""
The parts "value-if-true" and "value-if-false" are string expressions.
Examples
$a = "<date hh>" >= "12"? "afternoon": "morning"; msg "Good $a!";
$a = ("<date mm-dd>" == "12-24")? "": "not "; msg "It's $a"."X-mas!";
You can employ ternary conditionals in any argument or part of argument:
msg "Good " . ("<date hh>" >= "12"? "afternoon": "morning") . "!";
msg "It's " . ("<date mm-dd>" == "12-24"? "": "not") . " X-mas!";
The Compound Assignment Operators .=, +=, -=, *=, /=, \= can be used to shortcut operations of two variables where the result is set to one of the variables. For example, both lines below are functionally identical; the latter one uses one of the Compound Assignment Operators:
$a = $a . "b";
$a .= "b";
More examples:
$a = "a"; $a .= "b"; echo $a; //ab
$a = 1; $a += 1; echo $a; //2
$a = 1; $a -= 1; echo $a; //0
$a = 2; $a *= 3; echo $a; //6
$a = 5; $a /= 2; echo $a; //2.5
$a = 5; $a \= 2; echo $a; //2
Scripting offers various Control Structures by which you can control the order in which the individual statements are executed. Within these structures blocks of statements are grouped by encapsulating them with curly braces.
General syntax:
if (expression)
{
statement;
}
elseif (expression)
{
statement;
}
else {
statement;
}
If expression evaluates to TRUE, the following statement block is executed and the other blocks are ignored. If expression evaluates to FALSE the following statement block is ignored and processing continues with the next Elseif or Else block.
Remarks
· | Parentheses around the expression are mandatory. |
· | Curly braces around the statement block are mandatory (even if there is only one statement). |
· | The Elseif block(s) and Else block are optional. There can be only one Else block and it must be the last block in the whole control structure. |
Examples
if (1 == 1) {echo "Hi!"}
// shows
"Relax."
if (1 == 2) {
echo "Help!";
}
else
{
echo "Relax.";
}
// shows "else", "elseif",
"if"
$i = 1;
while ($i) {
if ($i
== 3) {
echo "if";
break 2;
}
elseif ($i == 2) {
echo "elseif";
}
else {
echo "else";
}
$i++;
}
General syntax:
while (expression)
{
statement;
}
The nested statement(s) are executed repeatedly, as long as the while expression evaluates to TRUE. The value of the expression is checked each time at the beginning of the loop. If the while expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once.
Remarks
· | Parentheses around the expression are mandatory. |
· | Curly braces around the statement block are mandatory (even if there is only one statement). |
Examples
// will show 1, then 2, then
terminate the script
while ($i < 2)
{$i++; echo $i;};
// will show 1, 2, 3, then
terminate the script
$x =
3;
$i = 1;
while ($i <= $x) {
echo $i;
$i++;
}
// nested while blocks are
okay
$x = 3;
$i = 1;
while ($i
<= $x) {
$w = "Word";
while ($w != "") {
echo "$w No. $i";
$w =
"";
}
$i++;
}
// expression is FALSE, message
will never show
while (1 == 2) {echo
"Help!"};
General syntax:
for (expression1; expression2;
expression3) {
statement(s);
}
The expression1 is executed once unconditionally at the beginning of the loop. In the beginning of each iteration, expression2 is evaluated. If it evaluates to true, the loop continues and the nested statement(s) are executed. If it evaluates to false, the execution of the loop ends. At the end of each iteration, expression3 is executed.
Remarks
· | Parentheses around the expressions are mandatory. |
· | Curly braces around the statement block are mandatory (even if there is only one statement). |
Internal Processing
For loops can be thought of as shorthand for While loops, and that's how they're supported now: For loops are internally converted to While loops. If you step through your scripts, you'll see that.
For example, this For loop:
for ($i = 1; $i <= 3; $i++)
{
echo $i;
}
... is internally converted to this While loop:
$i = 1;
while ($i <= 3) {
echo $i;
$i++;
}
General syntax:
foreach($variable,
ListOfTokens, [separator="|"], [flags], [MsgOnEmpty]) {
statement(s);
}
Remarks
· | $variable is the variable which receives the value of the next token in each round of the loop. You can use a new variable or an already used one, it does not matter. |
· | The last value of $variable remains even after the foreach loop. |
· | ListOfTokens is a string of tokens, separated by a separator. |
· | The separator defaults to "|"
(pipe), but can be set to anything, also multi-character
strings. Passing an empty separator will tokenize the ListOfTokens by letter. |
· | Surrounding spaces are not trimmed off on each side of the tokens. |
· | flags can be either empty (default) or set to "r" for "reverse direction", or "e" to skip empty items in the list of tokens. |
· | In MsgOnEmpty you can state an error message for the case that ListOfTokens is empty. If MsgOnEmpty is given (even as empty string "") the loop will not be executed at all. |
· | SC break and SC continue are supported. |
Examples
// returns 3 strings (moon,
sun, venus):
foreach($token,
"moon,sun,venus", ",") {
echo
$token;
}
// flag r: returns 3 strings in
reversed order (venus, sun, moon):
foreach($token, "moon,sun,venus", ",", "r")
{
echo $token;
}
// flag e: returns 2 strings
("moon", "venus"); empty items are skipped:
foreach($token, "moon,,venus,", ",", "e") {
echo $token;
}
// nested foreach
loops
foreach($token, "a|b|c")
{
foreach($token2, "1,2,3", ",")
{
echo $token .
$token2;
}
}
// selected list
items
foreach($token, <get
selecteditemspathnames |>) {
echo
$token;
}
// shows "No files selected!"
if no files are selected, and skips the loop
foreach($item, <get selecteditemspathnames>,
<crlf>, , "No files selected!") {
echo $item;
}
// passing an empty separator =
tokenize by letter
$string =
'string';
foreach($letter, $string, '')
{
echo $letter;
}
The switch statement is similar to a series of IF statements on the same expression. In many occasions, you may want to compare the same variable (or expression) with many different values, and execute a different piece of code depending on which value it equals to. This is exactly what the switch statement is for.
In a switch statement, the condition is evaluated only once and the result is compared to each case statement. In an elseif statement, the condition is evaluated again. If your condition is more complicated than a simple compare and/or is in a tight loop, a switch may be faster.
General syntax:
switch (n) {
case label1:
code to be executed
if n=label1;
break;
case label2:
code to be executed
if n=label2;
break;
case label3:
code to be executed
if n=label3;
break;
...
default:
code to be executed if n is
different from all labels;
}
Remarks
· | The case statements are checked from top to bottom until a match is found. |
· | Each case should be closed by a break statement. |
· | It's possible to use a semicolon instead of a colon after a case. |
· | A special case is the default case. This case matches anything that wasn't matched by the other cases. |
Examples
// switch
$favcolor = "blue";
switch ($favcolor) {
case "red":
echo "Your favorite
color is red!";
break;
case "blue":
echo "Your favorite color is blue!";
break;
default:
echo "Your favorite color
is neither red nor blue but $favcolor!";
}
echo "Switch
done!";
The switch and case arguments can also be more complex expressions:
$a = 3; $b = 7; $c = 10.5; $d =
2;
switch ($a * $b) {
case $c * $d:
echo "The result equals $c * $d = " . $c * $d;
break;
default:
echo "The result is
something else.";
}
Usually each case should be closed by a break statement. Else processing continues with the next case (sometimes though this can be desired). Here is an example where omitting break statements makes sense. Also shows that the default case also accepts a break statement (although is totally superfluous here). Also shows that semicolons after the cases are an acceptable alternative to colons:
$beer =
'Kirin';
switch($beer) {
case 'Asahi';
case 'Kirin';
case
'Sapporo';
echo 'Good
choice';
break;
default;
echo 'Please make a new selection...';
break;
}
The case statements don't have to be in their own line. This example also shows that using the return command within a function you can spare the break command:
function
getChineseZodiac($year){
switch ($year
% 12) {
case 0: return 'Monkey';
// Years 0, 12, 1200, 2004...
case
1: return 'Rooster';
case 2:
return 'Dog';
case 3: return
'Boar';
case 4: return
'Rat';
case 5: return
'Ox';
case 6: return
'Tiger';
case 7: return
'Rabbit';
case 8: return
'Dragon';
case 9: return
'Snake';
case 10: return
'Horse';
case 11: return
'Lamb';
}
}
echo
getChineseZodiac(2016);
// BTW, user functions work in switch and in
case:
switch (getChineseZodiac(2016))
{
case getChineseZodiac(2015): echo
"2015"; break;
case
getChineseZodiac(2016): echo "2016"; break;
case getChineseZodiac(2017): echo "2017";
break;
}
Also note this somehow perverted use of a switch:
$a = "Kirin";
switch (true){
case
$a=="Kirin": echo "first choice"; break;
case $a=="Sapporo": echo "second choice"; break;
}
Heredoc, using the <<< operator, is a way to define multi-line strings "as is" without the need to escape quotes etc.
The rules follow the PHP rules for Heredoc:
(1) | After the <<< operator, an identifier (your free choice) is provided, then a new line. The string itself follows, and then the same identifier again to close the quotation. The closing identifier must begin in the *first* column of the line (no indent!). The line with the closing identifier must contain no other characters, except possibly a semicolon (;) directly after the identifier. |
(2) | Heredocs overwrite comments, and comments overwrite heredocs, depending on who comes first. |
Within the Heredoc section:
(3) | Line feeds, empty lines, and all kinds of comments survive. |
(4) | Lines are not trimmed (leading and trailing spaces are preserved). |
(5) | Quoting is handled automatically (no need to add outer quotes or to double inner quotes). |
(6) | Variables are resolved. |
Example
A multiline script using Heredoc. Note that the Heredoc block is not indented, that comments are not removed, that quotes are not doubled, and that the variables are resolved (the only difference to Nowdoc), even within comments.
Basic example:
$str =
<<<EOD
Example of
string
spanning multiple
lines
using heredoc syntax.
EOD;
echo
$str;
More complex example, with variables and comments:
$name = "Bond";
text <<<FOO
My
name is $name, "James $name". // line end comment: $name
Follow /* note:
there's one space after me, */ me,
FOO
."please!";
Output:
My name
is Bond, "James Bond". // line end comment: Bond
Follow /* note: there's one space after me, */ me,
please!
Alternative Heredoc Syntax
A more radical parsing where the ending identifier can be anywhere is enabled when the identifier starts with a "#". These examples show two ways to code the same script.
$isOk = Confirm
(<<<#FOO
Blah
BlahFOO#FOO); echo $isOk;
With the old-school parsing the ending identifier must be on its own line:
$isOk = Confirm
(<<<FOO
Blah
BlahFOO
FOO
); echo
$isOk;
Nowdoc Syntax
A special variety of Heredoc is the Nowdoc, that is a Heredoc without interpolation. Quoting from PHP Documentation:
"Nowdocs are to single-quoted strings what heredocs are to double-quoted strings. A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc. The construct is ideal for embedding PHP code or other large blocks of text without the need for escaping. It shares some features in common with the SGML <![CDATA[ ]]> construct, in that it declares a block of text which is not for parsing.
A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier which follows is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc identifiers also apply to nowdoc identifiers, especially those regarding the appearance of the closing identifier."
Example:
$var =
"syntax";
$str =
<<<'EOD'
Example of
string
spanning multiple
lines
using nowdoc $var.
EOD;
echo
$str;
Output (note that $var has not been resolved):
Example
of string
spanning
multiple lines
using
nowdoc $var.
Nowdoc also accepts the alternative radical option (see above) enabled by prefixing the identifier with '#' (within the quotes).
You can use the dereference operator * (asterisk) with all variables. Usage: Helpful when dynamically creating variables.
Examples
$var = '$a'; *$var = "TEST"; echo $a; //TEST
$var = '$a'; perm *$var = "TEST"; echo $a; //TEST
$var = '$a'; global *$var = "TEST"; echo $a; //TEST
$var = '$a'; *$var = "TEST"; echo "*$var, $a!"; //TEST, TEST!
If the dereferenced variable is not defined, then the variable itself is used (as if there was no dereference operator):
*$undefined = "TEST"; echo $undefined; //TEST
Note that also unset and incr support the dereference operator:
$var = '$a'; *$var = "TEST"; unset *$var; echo $a; //$a
$var = '$a'; *$var = 1; incr *$var; echo $a; //2
$var = '$a'; *$var = 1; *$var++; echo $a; //2
$var = '$a'; *$var = 1; *$var--; echo $a; //0
$var = '$a'; *$var = 1; echo 1 + *$var++; echo *$var; //2; 2
Note that dereferencing is also supported in interpolation (including HEREDOC) if you explicitly allow it with the aid command.
The precedence of an operator specifies how "tightly" it is connected to its surrounding operands. For example, in the expression 1 + 2 * 3, the answer is 7 and not 9 because the multiplication ("*") operator has a higher precedence than the addition ("+") operator. Parentheses may be used to force precedence, if necessary. For instance: (1 + 2) * 3 evaluates to 9.
The following table lists the precedence of operators with the highest-precedence operators listed at the top of the table. Operators on the same line are evaluated from left to right (3 - 2 + 1 evaluates to 2, not 0):
Operators Additional Information
-------------------------------------------
++ -- Math: Increment, Decrement
! Not Boolean: NOT
+ - Math: Unary Plus, Unary Minus (operator is prefixed to a number)
* / *= /= Math: Multiply, Divide
% Math: Modulo
\ \= Math: Integer division
+ - += -= Math: Add, Subtract
. .= String concatenator
< <= > >= == != Comparison
Like LikeI UnLike UnLikeI Comparison
&& Boolean: AND
|| Boolean: OR
And Boolean: AND
Xor Boolean: XOR
Or Boolean: OR
? : Ternary
= Set
*** For software developers only ***
You can run an XYplorer script from an external program using the WM_COPYDATA command with XYplorer's hWnd. This means if you are a programmer you can fully remote control XYplorer.
· | cds.dwData: 4194305 (0x00400001) |
· | cds.lpData: The syntax is identical to the one of the command line switch /script=<script resource>, so you can either pass the path to a script file (commonly called *.xys), or pass the script directly (must be preceded by ::). |
Like other aspects of XYplorer scripting also user-defined functions are closely modeled after PHP.
A user-defined function (or simply "user function") is a block of statements that can be used repeatedly in a program. It can return a value (using the command "return"). The function declaration starts with the word "function", then a space, then the name of the function:
General form:
function functionName()
{
code to be executed;
}
Alternative formatting:
function functionName() { code to be executed; }
Example with 2 arguments and a return value:
function sum($x, $y)
{
$z = $x + $y;
return $z;
}
· | User function names can only consist of letters (a-z, A-Z), numbers, and underscores, and must not start with a number. |
· | User function names are NOT case-sensitive. |
· | The first line of user function declarations can be left-bound or indented. It does not matter. |
· | User functions can be defined anywhere in a script resource, before or after the main script, and as many as you want. |
· | A user function will not execute immediately when a script is loaded. It has to be called just like native functions. |
· | All user functions have global scope. |
· | User functions overwrite native functions of the same name. |
· | If two or more user functions share the same name, the *first* one declared will be used, all other ones ignored. |
· | User functions do not support function overloading, nor is it possible to undefine or redefine previously-declared functions. |
· | User functions can call each other. |
· | It is possible to call recursive user functions, but be aware that too many recursions can smash the stack and cause a termination of the current script. |
· | Variables are by default passed by value, not by reference (so the function cannot modify the variables in the caller's scope). |
· | To pass variables by reference (so the function can modify the variables in the caller's scope) prefix them with & in the function declaration. |
· | All arguments are optional. Default values for missing arguments can be set. If no default value is set a missing argument is initialized to "". |
· | XYplorer native variables (e.g. <crlf>) and environment variables (e.g. %tmp%) are allowed in function argument default parameters. They are resolved if they are unquoted or double-quoted. They are NOT resolved if they are single-quoted. |
Example 1: Arguments and Returns
echo multiply(3, 4) .
<crlf> . sum(11,12);
function
multiply($x, $y) { return $x * $y; }
function sum($x, $y) {
$z = $x + $y;
return
$z;
}
Example 2: Argument by Value
$a = 1; add_one($a); echo $a;
//1
function add_one($a) {
$a++;
}
Example 3: Argument by Reference
$a = 1; add_one($a); echo $a;
//2
function add_one(&$a)
{
$a++;
}
Example 4: Argument with Default Value
echo multiplyDefaults(2); //8
(2 * 4)
function multiplyDefaults($x = 3, $y
= 4) {
return $x * $y;
}
The "include" statement lets you include the content of a file at the place where the "include" statement was found. That way you can include e.g. function libraries into your script resources.
Note that the "include" statement is evaluated unconditionally when a script resource is loaded and before anything else is done.
Syntax
include file;
file: Path, if not fully stated, is resolved relative to <xyscripts>.
Extension defaults to ".xys".
Quotes are optional (they are totally function-less here, but might look better to some folks including me).
Note that you cannot use any script variables for file. However, XYplorer variables and environment variables are supported.
Remarks
You may end any include statement line with the usual instruction terminator ";". However, you must not append other instructions in the same line. The include statement needs to have its own line.
Include statements can be nested (included files can themselves include other files). The maximum nesting level for Include statements is 100 (one hundred). You will get an error if you go beyond. Beware of recursion: A file must not include itself or any file by which it has been included, else you will reach the maximum nesting level within the next millisecond...
Include statements can be indented. Included stuff will inherit the indent of the include statement.
Example (loaded script)
"Test 1"
echo multiply(3, 4);
"Test 2"
echo
divide(10, 3);
include "math.inc"
Example (included library):
// math.inc
function sum($x, $y) { return $x + $y; }
function diff($x, $y) { return $x - $y; }
function multiply($x, $y) { return $x * $y;
}
function divide($x, $y) { return $x / $y;
}
The "include_once" statement is identical to the "include" statement, with the only difference being that if the code from a file has already been included, it will not be included again. There are no errors or messages. Trying to include_once an already included file will simply ignore the statement and continue.
There is auto-include for scripts that are run directly from the address bar (and only from there). This way you can also use user functions from the address bar (which doesn't allow a proper include statement). It works like this:
· | Create a file named "xy-autoinclude.xys" in path <xyscripts>. |
· | Fill it with the user functions you intend to use. For example: |
function half($a) {return
$a/2}
function sum($x, $y)
{
$z = $x + $y;
return "$x + $y = $z";
}
· | Now you can run these commands right from the address bar: |
echo half(7); //returns 3.5
echo sum(172, 428); //returns "172 + 428 = 600"
Note: If "xy-autoinclude.xys" is not found when running a script from the address bar, XYplorer will not try again during that session. Saves speed, energy and material.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现