Equivalent to Unix eval in Windows - windows

Was wondering how you'd do the following in Windows:
From a c shell script (extension csh), I'm running a Python script within an 'eval' method so that the output from the script affects the shell environment. Looks like this:
eval `python -c "import sys; run_my_code_here(); "`
Was wondering how I would do something like the eval statement in Windows using Windows' built in CMD shell. I want to run a Python script within a Windows script and have the script run what the Python script prints out.
** update: specified interest in running from CMD shell.

If it's in cmd.exe, using a temporary file is the only option [that I know of]:
python -c "print(\"Hi\")" > temp.cmd
call temp.cmd
del temp.cmd

(Making some guesses where details are missing from your question)
In CMD, when a batch script modifies the environment, the default behavior is that it modifies the environment of the CMD process that is executing it.
Now, if you have a batch script that calls another batch script, there are 3 ways to do it.
execute the batch file directly:
REM call q.bat
q.bat
REM this line never runs
Usually you don't want this, because it won't return to the calling batch script. This is more like goto than gosub. The CMD process just switches from one script to another.
execute with call:
REM call q.bat
CALL q.bat
REM changes that q.bat affects will appear here.
This is the most common way for one batch file to call another. When q.bat exits, control will return to the caller. Since this is the same CMD process, changes to the environment will still be there.
Note: If q.bat uses the EXIT statement, it can cause the CMD process to terminate, without returning control to the calling script.
Note 2: If q.bat uses EXIT /B, then the CMD process will not exit. This is useful for setting ERRORLEVEL.
Execute in a new CMD process:
REM call q.bat
CMD /C q.bat
REM environment changes in q.bat don't affect me
Since q.bat run ins a new CMD process, it affects the environment of that process, and not the CMD that the caller is running in.
Note: If q.bat uses EXIT, it won't terminate the process of the caller.
The SETLOCAL CMD command will create a new environment for the current script. Changes in that environment won't affect the caller. In general, SETLOCAL is a good practice, to avoid leaking environment changes by accident.
To use SETLOCAL and still push environment changes to the calling script, end the script with:
ENDLOCAL && SET X=%X% && SET Y=%Y%
This will push the values of X and Y to the parent environment.
If on the other hand you want to run another process (not a CMD script) and have it affect the current script's environment, than have the tool generate a batch file that makes the changes you want, then execute that batch file.
REM q.exe will write %TEMP%\runme.cmd, which looks like:
REM set X=Y
q.exe
call "%TEMP%\runme.cmd"

Related

cmd redirection: equivalent of bash exec >file

In bash, with
exec >file
all output from stdout will be redirected.
Is there an equivalence in windows cmd.exe?
== P.S. ==
Why need it?
In our application, which needed to support embedded shell(cmd.exe on windows), to achieve better text search support, we decided to decouple command execution from output display.
Under bash, we have used exec >file to achieve that, which works seamlessly. But my cmd expertise is insufficient to find an equivalent command under windows.
== EDIT ==
to clarify what exec >file do in bash
after exec >file in bash, file descriptor 1, i.e., its stdout being redirected to file. which is different from mycommand >file.
In former case, stdout of shell itself being redirected, stdout of any command running inside it has not being technically redirected, but since it's child process of shell, the stdout is inherited, thus run ls after exec >file, ls's output will write to `file.
In latter case, stdout of shell itself has unchanged, only child process's stdout being redirected (by shell, its parent process).
In summary, what I ask is how to redirect cmd.exe itself's stdout, then use cmd.exe interactively just as normal, except cmd.exe don't write anything to console, all output write to file.
Thus, below approaches is not what i pursuit
dir >file
wrap_several_command_to_batch_file >file
wrap_several_command_to_batch_file_and_redirect_inside_then_exit
There is no equivalent to the exec command.
But it can be emulated by restarting the own batch file.
This works only for all commands in the batch file itself, but ends when the batch ends.
#echo off
REM *** Trampoline jump for function calls of the form ex. "C:\:function:\..\ownBatchFile.bat"
FOR /F "tokens=3 delims=:" %%L in ("%~0") DO goto :%%L
REM *** Emulated exec
REM *** Restart this batch file with the function name inside %0
REM *** Parameters are appended, but works only for simple parameters
call "%~d0\:exec:\..\%~pnx0" %* > file
exit /b
:exec
echo This will be redirected
If you want to redirect also from the command line, there are two different possibilities.
Start cmd with a redirection.
cmd /k > file
Disturb the file handles, by redirection restore failures.
The redirection works, but can't be restored later (only by exit the cmd instance).
Read more at More fun with redirection and file handles
echo dummy >NUL 2> nul 3> file
The second example works, because when there are multiple redirections at a time, then the restoring of the file handles fails, because the order of restoring is done in the wrong order by cmd.exe.

Windows Piping to a file from CMD

Quick question that I couldn't find an answer to. When piping to a file as such:
echo "hello" > hello.txt
does this operation actually call notepad.exe on Windows, or is this a non application level file operation?
No, echo is a builtin command in the windows Command Prompt (cmd.exe) used to display/output messages.
The output redirector, >, followed by a file path makes cmd.exe write the message to that file instead of to your screen.
You can see the full list of builtin commands in cmd.exe by typing help at the command prompt
echo is an internal command, which means it is built in to the windows command shell cmd.exe.
Source Internal commands:
The Windows CMD shell CMD.exe contains a number of 'internal' commands.
...
ASSOC, BREAK, CALL ,CD/CHDIR, CLS, COLOR, COPY, DATE, DEL, DIR, DPATH,
ECHO, ENDLOCAL, ERASE, EXIT, FOR, FTYPE, GOTO, IF, KEYS, MD/MKDIR,
MKLINK (vista and above), MOVE, PATH, PAUSE, POPD, PROMPT, PUSHD, REM,
REN/RENAME, RD/RMDIR, SET, SETLOCAL, SHIFT, START, TIME, TITLE, TYPE,
VER, VERIFY, VOL
Piping (a form of redirection) is also performed by the windows command shell cmd.exe.
See Redirection for more information.
The means that when you execute echo "hello" > hello.txt the whole of the command (the echo followed by the redirection is performed by cmd.exe.

Get cmd to re-evaluate environment variables

Assuming I have the following set as system environment variables in Windows 7
FOO = foo
path = ...;%FOO%/bin
Take the following example run in cmd
set FOO=bar
start cmd
echo %FOO%
//bar
echo %path%
//...;foo/bin
The path environment variable did not re evaluate itself upon launch of cmd, however the new FOO variable did stick. How can I get path to reevaulate itself based on the new FOO variable set in the parent command terminal?
EDIT: I'm looking for path to become ...;bar/bin
There is no "command line equivalent of opening cmd.exe from the desktop or start menu" that reproduces the behavior you care about. Environment variables are inherited from the parent process.
The shell reads the registry and performs interpolation (using its own environment, which is the process of being read from the registry, and knows nothing of variables you set in a command interpreter), which is why updates to the registry settings are reflected in a cmd.exe launched from the shell.
If you launch a new cmd.exe from a running cmd.exe, you won't get the shell behavior, and you will get the existing environment inherited. There's nothing in Windows that uses variables in a command interpreter to interpolate the registry settings. The code responsible for reading the environment from the registry is completely unrelated to cmd.exe... it is in explorer.exe (or probably one of the shell DLLs used by explorer).
This answer, which uses VB Script to read the registry and construct a batch file, is as good as you can get. I haven't tested whether interpolation is performed in the registry-access COM component (Environment("System") method on a WScript.Shell object) used by VB script, or if the environment variable references survive into the batch file and are interpolated during batch processing. So you may be confounded by the order of evaluations and variable assignments, in which case you'd better adapt the script to fetch just the PATH setting itself and leave all other variables alone.
I also found this to be useful
set path=%path:foo/bin=bar/bin%
Not as dynamic as I wanted but it works to replace a portion of a variable.

call "exit" from within shell script

I'm trying to write a simple script (for an embedded system) that mounts an external file from the network and then calls exit on the command line (which exits the busybox terminal and boots the system from the newly-mounted root directory). Problem is, when I call exit from the shell script it exits the script, not the outer terminal. Any hints as to how to send the exit command outside the script?
My code looks something like this:
#!/bin/bash
mount /mnt/root 1.2.3.4:/path/to/external/files -o nolock
exit # exits the script, not the outside filesystem!
exit does indeed exit the current shell. However, running the script creates a new process, and it is that process that exits, not the shell from which you called the script.
You probably want to simply source the file:
$ source myScript.sh
Then myScript.sh is executed in the current shell, rather than a spawned process, and the exit will exit from your current shell (and therefore the terminal itself).
If you want that the scripts exits the outer terminal call it like this:
source your_script
or just
. your_script
where the . is the same as the source command. You can follow this article if you need more information.
Explanation: source will execute the script in the current shell and therefore the exit will be interpreted by the current shell - what will close the pseudo(!) terminal window, if the shell was the first shell in tree

Calling vcvars from bash script

I need to call vcvars32.bat and vcvars64.bat from within the same bash script (msys) which builds different version of my application.
The problem is that, even if I am able to call the batch files with the cmd.exe command, once it returns the Visual Studio variables are obviously not set.
I cannot call vcvars from an external batch file (like msys.bat) which call the bash script, as I need in the same script to call both of them sequentially.
So, is there any way to call vcvars in order to properly set the variables in the bash script while running?
One way to solve this is to run your commands from within the vcvars environment, rather than trying to export it back to the bash side. That's the approach we've chosen for our project.
The main problem is that vcvars*.bat doesn't accept commands to execute in the environment, so a little bit of trickery with cmd is required. So I came up with a simple Bash script called vcvars_env_run.sh that accepts arbitrary arguments and forwards them to a cmd.exe on which vcvars64.bat has been called. The bulk of the work is figuring out how to properly forward quoted arguments, and things like &&, ||, return codes, etc.
I've uploaded the script and some examples at https://github.com/kromain/wsl-utils
You might need to tweak it a little to switch between vcvars32.bat and vcvars64.bat, but hopefully it helps for what you're trying to do.
The POSIX shell export -p prints the values of all environment variables such that an eval of the output recreates those values. The idea is to invoke this from a subshell after vcvars*.bat has run and to eval the captured output in the top-level script.
A Cygwin environment variable setup script can obtain the results of vcvars*.bat as follows:
eval "$($(cygpath "$COMSPEC") /c vcvars_export "$vcvars_bat" "$(
cygpath -w "$SHELL")")"
where vcvars_bat contains the path of the relevant vcvars*.bat file. You will find this value e.g. in the target of the x64 Native Tools Command Prompt for VS 2019.lnk file, which can be read with readlink -t. (Note also that the vcvarsall.bat script allows specification of the compiler and SDK version to use, useful when precise control is needed).
The local vcvars_export.bat file contains
#echo off
call %1 > nul
"%2" -c "export -p"
Limitations of the Cygwin native process invocation subsystem (argument quoting is done heuristically) make this auxiliary file necessary.
What you need to do is to use the command: "call" in your batch script. So it could look something like:
call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat"
echo DevEnvDir set to: %DevEnvDir%
If you don't use the "call" then the script will exit after the vcvars32.bat exits and won't run any other command.

Resources