我見過:
- 如何在不等待批處理檔案的情況下啟動應用程式?
- 程式啟動后,如何在不打開控制臺的情況下從批處理檔案運行程式?
- 如何從批處理檔案中獲取剛剛啟動的行程的PID?
- 有什么方法可以重定向 Windows 命令列中使用“start”運行的命令的 stderr 輸出?
...但我仍然無法真正得到我想要的作業,所以這是我的問題。
我有一個基本上永遠回圈的程式(直到被 Ctrl-C 中斷),并將日志訊息輸出到stderr; 這是一個例子,testlogerr.c:
// based on https://stackoverflow.com/questions/26965508/infinite-while-loop-and-control-c
// can be compiled in MINGW64 with:
// gcc -g testlogerr.c -o testlogerr.exe
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
volatile sig_atomic_t stop;
void inthand(int signum) {
stop = 1;
}
int main(int argc, char **argv) {
signal(SIGINT, inthand);
int counter = 0;
while(!stop) {
fprintf( stderr, "%d: Logging line %d\n", (int)time(NULL), counter );
sleep(2);
}
printf("exiting safely\n");
//system("pause"); // does "Press any key to continue . . ."; skip here
return 0;
}
現在,將這個程式(在 MINGW64 中)構建為 Windows .exe,我想通過批處理腳本cmd.exe作為后臺行程啟動它,將其 stderr 重定向到日志檔案,并獲取其 PID 作為行程。要做到這一點,在 Linux 中bash我會簡單地做(另見https://unix.stackexchange.com/questions/74520/can-i-redirect-output-to-a-log-file-and-background-a- process-at-the-same-time,如何獲取后臺行程的行程ID?):
testlogerr > myfile.log 2>&1 &
TESTLOGERR_PID=$!
echo "testlogerr started, its PID is $TESTLOGERR_PID"
我的問題是:如何在批處理腳本中執行相同的操作,以便我只cmd.exe啟動一個視窗,并且在后臺行程啟動后在該視窗中收到提示?
As far as I've seen from the links above, start /b would start a command in background - but then one cannot obtain the PID of the background process.
Furthermore, https://stackoverflow.com/a/59971707/6197439 recommends PowerShell, so I tried the following, e.g. as testlogerr.bat:
powershell -executionPolicy bypass -command ^
"& {$process = start-process $args[0] -passthru -argumentlist $args[1..($args.length-1)]; exit $process.id}" ^
testlogerr.exe 2>testlogerr.log
... however, the problem is - when I double-click this testlogerr.bat file in Windows Explorer:
- First one
cmd.exeterminal window gets started, then it closes, and thetestlogerr.exegets started in anothercmd.exewindow - The
cmd.exeterminal window wheretestlogerr.exeruns, shows nocmd.exeprompt - instead, it shows stderr log messages; meaning it is running in foreground, not background- Another indication of foreground run, is that when I hit Ctrl-C,
testlogerr.exeexits - and so does its terminalcmd.exewindow
- Another indication of foreground run, is that when I hit Ctrl-C,
- The
testlogerr.logfile gets created, but its empty
So - how can I start the program as a background process, redirecting its stderr to file, obtain and print its pid, and finally show a cmd.exe terminal prompt (while the started process runs in the background) - all in a single cmd.exe terminal window?
uj5u.com熱心網友回復:
在 Linux shell 中,$!存盤最后執行的PID. Powershell 可以通過使用實作相同$process.id的當前PowerShell代碼僅存在于PID盡管中,并且永遠不會顯示。因此更改批處理檔案中的代碼以用于Write-Host顯示 PID(類似于echo在 bash 中):
@echo off
powershell -executionPolicy bypass -command "& {$process = start-process $args[0] -passthru -argumentlist $args[1..($args.length-1)]; Write-Host testlogerr started, its PID is $process.id}" testlogerr.exe 2>testlogerr.log
pause>nul
PS!!如果您想要stdout和 都stderr在日志檔案中,則更2>testlogerr.log改為>testlogerr.log 2>&1
uj5u.com熱心網友回復:
我想我終于得到了這個 - 經過大量失敗的嘗試......
首先,讓我注意到我期望“行緩沖”重定向;但是,Windows 不支持它,它要么具有“字符緩沖”I/O(串行埠),要么緩沖所有內容(直到行程退出,然后將其輸出重繪 到檔案中)。但是,緩沖發生在程式的輸出 - 所以為了確保我的程式沒有緩沖,我進行了這些更改(如果您無法控制要以這種方式運行的程式,請參閱其中一個參考鏈接他們建議在以下位置使用winpty“unbuffer”ing) testlogerr.c:
// based on https://stackoverflow.com/questions/26965508/infinite-while-loop-and-control-c
// can be compiled in MINGW64 with:
// gcc -g testlogerr.c -o testlogerr.exe
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
volatile sig_atomic_t stop;
void inthand(int signum) {
stop = 1;
}
int main(int argc, char **argv) {
// disable output/line buffering:
// https://stackoverflow.com/questions/8192318/why-does-delayed-expansion-fail-when-inside-a-piped-block-of-code#8194279
// https://stackoverflow.com/questions/40487671/is-out-host-buffering
// https://stackoverflow.com/questions/11516258/what-is-the-equivalent-of-unbuffer-program-on-windows
// https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
signal(SIGINT, inthand);
int counter = 0;
while(!stop) {
fprintf( stderr, "%d: Logging line %d\n", (int)time(NULL), counter );
sleep(2);
}
printf("exiting safely\n");
//system("pause"); // does "Press any key to continue . . ."; skip here
return 0;
}
是的,現在我們有一個程式可以將“無緩沖”(即“字符緩沖”)寫入 stderr,這是對我有用的批處理檔案,testlogerr.bat-首先是作業部分,然后是參考,其他所有不起作用的部分為了我:
:: so, the only way to prevent Ctrl-C, AND run in the same cmd.exe window started by .bat, AND obtain the PID of the background process, is to use start /b - and then, retrieve the PID from the difference in started tasks ..
:: https://stackoverflow.com/questions/4677462/windows-batch-file-pid-of-last-process
@echo off
tasklist /FI "imagename eq testlogerr.exe" /NH /FO csv > task-before.txt
start /b testlogerr.exe > testlogerr.log 2>&1
tasklist /FI "imagename eq testlogerr.exe" /NH /FO csv > task-after.txt
for /f "delims=, tokens=2,*" %%A in ('"fc /L /LB1 task-before.txt task-after.txt | find /I "testlogerr.exe""') do set TESTPID=%%A
del task-before.txt
del task-after.txt
:: next command deletes double quotes (") from the string itself, so only the PID number remains:
SET TESTPID=%TESTPID:"=%
:: now, print the PID we obtained:
echo TESTPID is %TESTPID%
:: note that at this point, the started terminal actually blocks!
:: so here we run cmd.exe one more time, so we get the command prompt shell
cmd
:: to exit this cmd shell, first you have to do `taskkill /F /PID %TESTPID%`, and only then `exit` will work
:: (otherwise it blocks) - or, just close via the X button at upper right corner of the cmd.exe window
REM Failed approaches below:
REM :: there is -RedirectStandardError for powershell Start-Process;
REM :: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.1
REM :: that means we do not have to start cmd so we have stream redirection; however,
REM :: -RedirectStandardError causes the reading via for..in..do to freeze (otherwise the below works)
REM @echo off
REM for /F "delims=" %%i IN ('powershell -command "$proc = Start-Process testlogerr.exe -RedirectStandardError testlogerr.log -NoNewWindow -passthru ; Write-Output $proc.id"') DO set i=%%i
REM echo i was %i%
REM pause
REM :: also "batch macro" https://stackoverflow.com/q/69539939/ does not work,
REM :: when RedirectStandardError is there (but works otherwise)
REM @echo off
REM call :initMacro
REM %$set% TESTPID="powershell -command "$proc = Start-Process testlogerr.exe 2>testlogerr.log -NoNewWindow -passthru ; Write-Output $proc.id""
REM echo TESTPID %TESTPID[0]%
REM ...
REM :: piping to SET https://stackoverflow.com/q/8192318 also does not work:
REM powershell -command "$proc = Start-Process testlogerr.exe -RedirectStandardError testlogerr.log -NoNewWindow -passthru ; Write-Output $proc.id" | set /p TESTPID=
REM echo TESTPID %TESTPID%
REM pause
REM :: must redirect to files, then https://stackoverflow.com/q/8192318 - this works:
REM :: openfiles /local on :: requires system reboot!
REM :: note: somehow test.pid here ends up being held/"used by" testlogerr.exe? yes, see https://superuser.com/q/986202 ; that means we cannot really delete it (ugh!)
REM @echo off
REM powershell -command "$proc = Start-Process testlogerr.exe -RedirectStandardError testlogerr.log -NoNewWindow -passthru ; Write-Output $proc.id" > test.pid
REM set /p TESTPID=<test.pid
REM :: openfiles /query /fo table | findstr test.pid :: access denied
REM :: del test.pid
REM echo TESTPID %TESTPID%
REM pause
REM :: like this, though, test.pid is not kept under ownership, so we can easily delete it
REM @echo off
REM powershell -command "$proc = Start-Process testlogerr.exe -RedirectStandardError testlogerr.log -NoNewWindow -passthru ; Write-Output $proc.id | Out-File -Encoding ASCII -FilePath .\test.pid"
REM set /p TESTPID=<test.pid
REM del test.pid
REM echo TESTPID %TESTPID%
REM :: pause :: not needed anymore, no need for "Press any key to continue . . ."
REM :: note that at this point, the started terminal actually blocks!
REM :: so here we run cmd.exe one more time, so we get the command prompt shell
REM :: (however, even there, if we hit Ctrl-C, it will break our background program!)
REM cmd
REM :: as per https://superuser.com/q/1479119
REM :: like this, Ctrl-C does not kill the process by accident anymore;
REM :: however, the PID returned is for the cmd.exe, not the testlogerr.exe
REM @echo off
REM powershell -command "$proc = Start-Process -FilePath 'CMD.EXE' -ArgumentList '/C START /B testlogerr.exe' -RedirectStandardError testlogerr.log -NoNewWindow -passthru ; Write-Output $proc.id | Out-File -Encoding ASCII -FilePath .\test.pid"
REM set /p TESTPID=<test.pid
REM del test.pid
REM echo TESTPID %TESTPID%
REM cmd
REM :: so, the only way to prevent Ctrl-C, AND run in the same cmd.exe window started by .bat, AND obtain the PID of the background process, is to use start /b - and then, retrieve the PID from the difference in started tasks ..
REM :: https://stackoverflow.com/questions/4677462/windows-batch-file-pid-of-last-process
REM ( ... here was the working code, which has now been moved at start/top of snippet)
所以,這個.bat檔案現在允許我雙擊它,我將進入新啟動的cmd.exe終端視窗:
TESTPID is 5532
Microsoft Windows [Version 10.0.19043.1266]
(c) Microsoft Corporation. All rights reserved.
C:\tmp>
因此,該testlogerr.exe行程在后臺啟動 - 但如果我在新啟動的cmd.exe終端中不小心按了 Ctrl-C ,我將不會關閉testlogerr.exe。
此外,testlogerr.exe的日志訊息通過管道傳輸到testlogerr.log檔案中(我可以確認該日志檔案中的行一一出現 - 如果我們有 Linux tail,我們可以看到實時更新tail -f testlogerr.log)。
And finally, I get a cmd.exe shell prompt at end - which means, I can immediately inspect the situation:
C:\tmp>tasklist | findstr 5532
testlogerr.exe 5532 Console 1 3,148 K
Great, that works!
Unfortunately, I thought this would allow me to start two such terminals, with two separate instances of the background process - unfortunately, if I try to do that, I get in the second terminal:
The process cannot access the file because it is being used by another process.
TESTPID is "=
But, at least I got the behavior that I wanted in the OP/question ...
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/314212.html
標籤:windows batch-file cmd
