質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

コマンドプロンプト

コマンドプロンプト(cmd.exe)はMicrosoftによって提供されているコマンドラインインタプリタです。OS/2・Windows CE・Windows NTで使用可能です。

コマンドライン

コマンドライン(別名:Command Line Interface)は、ユーザに命令の入力を促す(プロンプト)文字列の表示を行い、すべての操作をキーボードを用いて文字列を打ち込む事でプログラムを走らせるユーザインターフェースです。

Q&A

解決済

2回答

1768閲覧

set /p を含むコマンドプロンプトバッチをパイプで適切に処理したい

terayuichi

総合スコア1

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

コマンドプロンプト

コマンドプロンプト(cmd.exe)はMicrosoftによって提供されているコマンドラインインタプリタです。OS/2・Windows CE・Windows NTで使用可能です。

コマンドライン

コマンドライン(別名:Command Line Interface)は、ユーザに命令の入力を促す(プロンプト)文字列の表示を行い、すべての操作をキーボードを用いて文字列を打ち込む事でプログラムを走らせるユーザインターフェースです。

0グッド

0クリップ

投稿2023/06/24 05:26

編集2023/06/24 05:28

実現したいこと

set /p を含むコマンドプロンプトバッチをパイプで適切に処理したいです。
具体的には、以下のバッチファイル abc.bat を

cmd

1@echo off 2set USR_INPUT_STR= 3set /P USR_INPUT_STR="あなたの名前は?:" 4echo %USR_INPUT_STR%さん。こんにちは。

以下のように実行して、コンソール+ファイル出力を同時に行いたいです。
powershell.exe -NoLogo -Command "cmd.exe /c 'abc.bat' | Tee-Object -FilePath 'abc.log'"

しかし | (パイプ) 処理を入れてしまうと「あなたの名前は?:」が表示されるタイミングがユーザーが入力した後になり、違和感のある振る舞いになってしまいます。パイプを用いる場合でも単独でabc.batが実行される状況と同じようにしたいです。どのような方法があるでしょうか?

前提条件

Windows環境
コマンドプロンプト + PowerShell

背景

任意の外部プログラム(上記の abc.bat が一例)の実行管理を行うプログラムを作成しています。
ポイントは tee の実現です。
外部プログラムは標準出力/エラー/入力すべて含まれる可能性があり、どのようなものも受け入れられるようにしたいものです。

補足

cmd.exe /c 'abc.bat' 2>&1 としても効果はありませんでした。
powershell に限らずコマンドプロンプトでもパイプで処理すると同じような状況になります(以下、例)。
set /p USR_INPUT_STR="あなたの名前は?:" | more

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

「あなたの名前は?:」をログファイルに書かなくて良いなら、

CMD

1set /P USR_INPUT_STR="あなたの名前は?:">&2

と、メッセージを標準出力じゃなくて標準エラー出力に出せば、パイプされずに端末に表示されます(別途、標準エラー出力のリダイレクトを行っていない場合)。これはUnix/Linuxのシェルスクリプトでもよく使うやり方です。
あるいは、あまり見ませんが>CONと端末にリダイレクトするか。

ログに書きたい場合は、簡単には改行してしまうことです。改行しないのは諦める。

CMD

1echo あなたの名前は?: 2set /P USR_INPUT_STR=

出力されない理由は、GPTの回答のとおり、バッファリングです。入出力のシステムコールは呼び出し1回毎の
オーバーヘッドがあるので、同じ1MBのデータをファイルに書いたり端末に表示したりする場合に、
・1バイトずつ100万回(1024*1024回)出力のシステムコールを呼ぶ
・1KBずつ1024回システムコールを呼ぶ
・1MBを1回だけシステムコールを呼ぶ
で格段にCPU使用量や処理時間が違ってきます。
(今ファイルコピーでやってみると、1バイトずつで1.5秒、1MBだと0.003秒)

一般にデータをコマンドがファイルに書いたり端末に書いたりするときには、

①データ発生の都度、すぐにそのまま出力する
②ある程度溜めてからまとめて出力する(一般には数KB。4KBとか)
③そうはいっても端末にまとめ出力するのも見にくいので、端末の場合だけは数KBに満たなくても改行が来たら出力する。端末表示は元々遅いので。ファイルへの出力は②と同じ

のどれかを採用しています。コマンド側でオプションで変更出来るケースもありますが、多くの場合は②か③です。①は上述のように遅い・重いので。

①なら、改行がなくても端末に表示されますが、今回は実際やってみると③のようで、GPTの言うことが正しいなら変更できません。
ということで、パイプ経由で端末にすぐ出したいなら改行する必要があります。
もちろん②を採用しているコマンドなら改行しても駄目です。

(Linuxだと①②③を選択できないコマンドを強制的に変更して実行する「コマンド実行コマンド」もあります。Windowsで可能かどうかは知りません)

投稿2023/06/24 10:12

編集2023/06/24 10:17
otn

総合スコア86277

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2023/06/24 10:33

今回はPowershellのパイプ処理の事情も絡んでるみたいですね。よくわからず。 上記①でそのまま出力するコマンドを使って、 元々のabc.bat | 上記①でそのまま端末に出力するコマンド.exe を実行した場合、 コマンドプロンプトで実行すると、「あなたの名前は?:」は先に表示される Powershellプロンプトで実行すると、「あなたの名前は?:」は後で表示される
terayuichi

2023/06/24 12:17 編集

ありがとうございます。大変参考になりました。 abc.bat を呼び出す以下の def.bat を作成して全体を標準エラー出力に出せば @echo off call abc.bat>&2 以下で完全に期待の結果が得られることがわかりました。 powershell.exe -NoLogo -Command "cmd.exe /c 'def.bat' | Tee-Object -FilePath 'abc.log'" ただ実際には >&2 ですべてを標準エラー出力してしまうと問題がある外部プログラムもあるので、この方法が採れるかは検討が必要ですが... まずは御礼申し上げます。
terayuichi

2023/06/24 12:20

> 上記①でそのまま端末に出力するコマンド.exe こちらですが、具体的にはどのようなものになるでしょうか?差し支えなければ具体的な名称やプログラムコード断片を提示いただければ嬉しいです。
otn

2023/06/24 13:41 編集

> call abc.bat>&2 これだと、def.batは標準出力に何も出力しないので、abc.log は空になると思いますが、それで良いのでしょうか? それで良いのなら、 powershell.exe -NoLogo -Command "cmd.exe /c abc.bat" でいいのでは? 上記①でそのまま端末に出力するコマンド.exe 私は、Linux系コマンドをWindows移植版MSYS2の中の、dd コマンドで、 dd bs=1 (1バイトずつ読み書き=溜めない) を使いました。Cで書くなら、 #include <unistd.h> int main(){ int c; while(read(0,&c,1)) write(1,&c,1);return 0;} とか。これも1バイトずつ読み書き。
terayuichi

2023/06/26 01:52

ありがとうございます。 >> call abc.bat>&2 > これだと、def.batは標準出力に何も出力しないので、abc.log は空になると思いますが、それで良いのでしょうか? その通りで abc.log は空でした。私の勘違いでした。申し訳ありません。 またCプログラムの提示、ありがとうございます。 teeを自作する可能性を考えて質問させていただきました。 ただ実際にはユーザー入力を適切に扱うなど難しい部分も多く、簡単ではないと考えています。
otn

2023/06/26 02:03

> teeを自作する可能性を考えて質問させていただきました。 > ただ実際にはユーザー入力を適切に扱うなど難しい部分も多く、簡単ではないと考えています。 よくわかりませんが、回答に書いた2案とも前提を満たさないと言うことでしょうか? 満たさないといけない前提が質問に書かれている以外にあるのであれば、書きましょう。 > teeを自作する可能性を考えて質問させていただきました。 2023/06/24 19:33 のコメントに書いたとおり、tee の問題というよりは、Powershellの問題なので、Poweshellから起動する限り、teeのところで何をしようが解決しませんよ。と言うことをコメントで言ったつもりですが、読み取れませんでしたか?
terayuichi

2023/06/26 05:00 編集

# 自身の回答で混乱やご不快な思いを与えたのであれば、その意図はもちろんなく、お詫びします。 # 完全な解決には至っていないのですが、少なくとも後述の制約事項を説明する上での貴重な情報をいただけているので、ベストアンサーマークを付けます。 「やりたいこと」は外部プログラムに対する tee 的な動作(コンソール出力 + ログファイルへの記録)の実現です。 PowerShellでの起動は、Windows標準でのtee実現手段としてTee-Objectコマンドレットが存在するからのものでした。他の手段で実現可能であればPowerShellを使う必要はありません。 ただその「他の手段」が自作にせよ何かのOSS等の利用にせよ、容易ではないだろうというのが私の今の考えです。なのでご回答には大変感謝しつつも、想定プログラムにおいては set /p の出力順に関しては制約事項として考えてもらおうと思っています。
otn

2023/06/26 11:19

> 完全な解決には至っていないのですが、 再度書きますが、 > よくわかりませんが、回答に書いた2案とも前提を満たさないと言うことでしょうか? どの点がどういう風に前提を満たさないのかを書いて貰うと、改善できる可能性もありますが、それを書かないままで諦めるのでしょうか?何故??
terayuichi

2023/06/26 15:48 編集

引き続き見ていただきありがとうございます。2案に関する現在の考えを下記します。 #「2案」を読み違えている場合はご指摘いただければ幸いです。 前提: 任意の外部プログラム(原則、変更不可)に対する tee (コンソール出力 + ファイル出力)の実現。外部プログラムの振る舞いはそのまま素で実行した場合と完全に同じようにしたい。 1. メッセージを標準出力じゃなくて標準エラー出力に出す。 : 全体を標準エラーに出した場合(call abc.bat>&2)、ご指摘のようにTee-Objectのファイル出力は空になり、 teeが満たせません。set /P USR_INPUT_STR="あなたの名前は?:" 行だけを標準エラー出力に変更することは変更不可の前提を満たせません。 2. (set /P を)簡単には改行してしまうことです。改行しないのは諦める : 変更不可の前提を満たせません。ただし外部プログラム提供元に有効な回避策として提示できるので、貴重な情報だと考えています。 前提をわかりやすく提示できていなかったことは申し訳ないです。
otn

2023/06/26 17:41 編集

失礼しました。 > 具体的には、以下のバッチファイル abc.bat を に注意が向いてしまっていて、 > 任意の外部プログラム を軽視していて「バッチファイルが変更不可」と思っていませんでした。 と言うことであれば、 ・teeを自作する(または何らかのLinux系ツール群のteeをインストールする(この場合は日本語処理に注意)) ・Powershellを使わず、コマンドプロンプトやバッチファイルでそれをパイプで呼び出す で実現できるかと思います。 ただし、「任意のプログラム」が、例のようにバッチファイルであり、問題となるのがset /pの改行無しプロンプトである等のケースの話で、全くの任意のプログラムで「標準出力が端末かパイプかで動作が変わるプログラム」であれば不可能です。
terayuichi

2023/06/28 03:10

ありがとうございます。 > 全くの任意のプログラムで「標準出力が端末かパイプかで動作が変わるプログラム」であれば不可能です。 このような見方はできていなかったので、非常に参考になりました。 可能性としてはもちろんあるので、外部プログラム提供者(ユーザー)には制約事項として伝えようと思います。
otn

2023/06/28 04:00

> 「標準出力が端末かパイプかで動作が変わるプログラム」 はUnix/Linuxコマンドだとよくあります。上記の③のケースが多いです。これだと無理。 コマンドプロンプトのset /q は改行がないのにパイプに出力するようなので ① ですね。これだとOK。 ②は、はなから駄目。
guest

0

この回答はGPT-4による自動生成回答です

この問題は、パイプ処理がバッファリングされるために発生しています。バッファリングを無効にする方法はありませんが、以下のような方法で回避できます。

  1. PowerShellのStart-Processコマンドレットを使用して、リダイレクトを行います。

powershell

1Start-Process -FilePath "cmd.exe" -ArgumentList "/c abc.bat" -RedirectStandardOutput "abc.log" -RedirectStandardError "abc.log" -NoNewWindow -Wait
  1. バッチファイル内でログファイルに出力するように変更します。

cmd

1@echo off 2set USR_INPUT_STR= 3set /P USR_INPUT_STR="あなたの名前は?:" 4echo %USR_INPUT_STR%さん。こんにちは。 | tee "abc.log"

そして、バッチファイルを通常の方法で実行します。

cmd

1cmd.exe /c abc.bat

これらの方法は、パイプを使用せずに標準出力と標準エラーをファイルにリダイレクトするため、バッファリングの問題が発生しません。ただし、これらの方法では標準出力と標準エラーが同じファイルにリダイレクトされるため、出力の順序が変わる可能性があります。

投稿2023/06/24 05:27

teratail-ai

Powered by GPT-4

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

terayuichi

2023/06/24 05:35

cmd には tee コマンドはありません。 またStart-Processのリダイレクトを用いると標準入力をユーザーに提供することができなくなります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問