我正在使用这样的批处理文件运行Powershell脚本:
auto identity = [](int n) constexpr
{
return n;
};
constexpr auto add = [](int x, int y)
{
auto L = [=] // what is = [=]?
{
return x;
};
auto R = [=]
{
return y;
};
return [=] // what return [=] means here?
{
return L() + R();
};
};
void ConstexprLambda()
{
static_assert(identity(123) == 123);
static_assert(add(1, 2)() == 3); // why can't we just add(1,2) like above?
}
此.ps1脚本可能需要15分钟才能运行。在这段时间内,我想避免再次运行此脚本。如果用户运行.bat文件,我希望控制台打印出类似“ Powershell脚本已在运行,请等到结束”之类的内容。
从各种示例中,我已经看到了这段代码:
@ECHO OFF
PowerShell.exe -Command "& 'U:\...\...\..._PS.ps1'"
PAUSE
它有2个问题: 1.它仅适用于.exe文件,例如记事本,powershell等...我具有.ps1的路径 2.它杀死了任务,我只需要显示一条消息
答案 0 :(得分:3)
如果坚持使用-Command
参数,则可以执行以下操作:
powershell.exe -Command "if (((Get-CimInstance Win32_Process -Filter \"name = 'powershell.exe'\").CommandLine -match '..._PS\.ps1').Count -gt 1) {'Powershell is already running.'} else {& 'U:\...\...\..._PS.ps1'}"
想法是检查当前正在运行的Win32_Process
项,这些项的命令行包含您的..._PS.ps1
文件。由于检查命令将在检查条件之前将自身添加到堆栈中,因此您必须检查1次以上的事件。由于.
使用正则表达式,因此在使用-match
运算符时,所有文字-match
字符都需要反斜杠转义。请注意内部双引号的反斜杠转义,因为您是从CMD Shell运行的。从PowerShell运行此命令将需要不同的转义符。
答案 1 :(得分:2)
您可能可以使用wmic来检查powershell.exe
进程,并以脚本名结尾的关联CommandLine
:
@(For /F "Delims==" %%A In (
'WMIC Process Where "Name='powershell.exe' And CommandLine Like '%%\\unknown[_]PS.ps1\"'" Get Name /Value'
)Do @If "%%A"=="Name" (
Echo "Powershell script is already running, wait until it's over"&Timeout 10 /NoBreak>NUL&Exit /B)) 2>NUL
@Powershell -ExecutionPolicy RemoteSigned -File "U:\...\...\unknown_PS.ps1"
由于您没有提供完全合格的实际文件路径和名称,因此需要自行调整此脚本。在5
行中,将U:\...\...\unknown_PS.ps1
替换为您的实际文件路径,并在unknown
行中将2
替换为_PS.ps1
的实际前缀。请注意,Like运算符使用下划线作为通配符,因此任何下划线都需要通过将其括在方括号中来进行转义,(对于_PS.ps1
中已知的下划线,我已经这样做了)。我认为仅检查以\unknown_PS.ps1"
结尾的命令行就足够了,尽管如果您希望具有相同文件名的多个powershell脚本可以将其扩展到完整路径。
答案 2 :(得分:0)
AdminOfThings's helpful answer和Compo's helpful answer均提供可靠的解决方案。
这是一个替代解决方案,如果您愿意做一个假设:
foo.cmd
,则发现执行名为cmd.exe
的批次文件的任何foo.cmd
进程确实在执行相同的批次文件。该解决方案利用了两个事实:
cmd.exe
控制台窗口将名称/路径(取决于调用细节)附加到窗口标题;例如,如果您要从当前目录调用foo.cmd
,则窗口标题将更改为Command Prompt - foo.cmd
。
tasklist.exe /v
还会输出匹配进程的主窗口标题。
@echo off
setlocal
rem # Count the cmd.exe processes that have the name of this batch file
rem # in their window title.
for /f %%c in ('tasklist /v /fi "imagename eq cmd.exe" ^| find /C "%~nx0"') do (
REM # If more than 1 process matches, another instance of this batch file
REM # must already be running, so issue a warning and exit.
if %%c GTR 1 echo Powershell script already running, wait for it to finish. >&2 & exit /b 1
)
echo Running PowerShell script...
rem # Run the PowerShell script.
rem # As @Compo advises, it's better to use -File to invoke scripts
rem # and to use -ExecutionPolicy Bypass if needed (and you trust the script).
powershell.exe -file U:\path\to\your\PowerShellScript.ps1
pause
您可以更加努力地使解决方案更强大:
为当前控制台窗口分配唯一的自定义窗口标题。
首先检查具有该标题的cmd.exe
进程:如果找到,请退出;如果没有,请设置唯一的窗口标题并启动PowerShell脚本。
注意事项:您必须在完成时重置窗口标题,否则它将超出批处理文件的执行范围:
面临的挑战是,如果有人使用 Ctrl + C 终止您的批处理文件,您将没有机会重置标题。
但是,如果您的批处理文件是在控制台窗口中启动的,那么该批处理文件将在终止时自动关闭,例如从桌面或文件资源管理器启动,那么这将不是问题。
@echo off
setlocal
rem # Get and save the current window title.
rem # Note: This - complex and somewhat slow - solution is necessary,
rem # because the `title` only supports *setting* a window title,
rem # not *getting it*.
for /f "usebackq delims=" %%t in (`powershell -noprofile -c "[Console]::Title.Replace(' - '+[Environment]::CommandLine,'') -replace '(.+) - .+', '$1'"`) do set origTitle=%%t
rem # Choose a unique window title.
set "titleWhileRunning=Some.ps1 is running..."
rem # If a cmd.exe process with the same window title already exists
rem # we issue a warning and exit
tasklist /v /fi "imagename eq cmd.exe" /fi "windowtitle eq %titleWhileRunning%" | find "%titleWhileRunning%" >NUL
if %ERRORLEVEL% EQU 0 echo Powershell script already running, wait for it to finish. >&2 & exit /b 1
rem # Set the new title that indicates that the script is running.
title %titleWhileRunning%
echo Running PowerShell script...
rem # Run the PowerShell script.
rem # As @Compo advises, it's better to use -File to invoke scripts
rem # and to use -ExecutionPolicy Bypass if needed (and you trust the script).
powershell.exe -file U:\path\to\your\PowerShellScript.ps1
rem # Restore the original window title.
title %origTitle%
pause
答案 3 :(得分:0)
由于使用的是PowerShell,因此还可以在PowerShell中创建系统范围的互斥锁,以防止同时执行脚本的多个实例。
@echo off
setlocal
:: This should be a predefined static unique value.
:: This is an example, you should generate your own GUID
set "MutexName=MyScript.PS1:{72a7654d-aa01-4e7e-a985-89add8524590}"
set "PSMsg=Powershell script is already running, wait until it''s over"
set "PSCode=[bool]$isOwner = $false; $mutex=[System.Threading.Mutex]::new($true , '%MutexName%', [ref]$isOwner)"
set "PSCode=%PSCode%; if($isOwner) {& '.\MyScript.ps1'} else {Write-Output '%PSMsg%'}; $mutex.close()"
PowerShell.exe -Command "iex ${env:PSCode}"
如果您可以控制PowerShell脚本的内容,则应将此方法直接合并到脚本本身中,而不是将脚本包装在另一个代码中
答案 4 :(得分:-1)
解决此问题的一种方法似乎是使用dbenham编写的lock file mechanism,该代码适合您的代码,
@ECHO OFF
CALL :main >>"%~f0"
EXIT /B
:main
PowerShell.exe -Command "& 'U:\...\...\..._PS.ps1'"
EXIT /B
然后,如果另一个进程尝试执行相同的batch-file
,它将失败。但是Powershell
可以执行.ps1
。因此,在这种情况下,最佳解决方案可能是在.ps1
内部使用相同的机制。