制御端末の本名を取得するにはどうすればよいですか?


13

制御端末の実際の名前(ある場合はエラー)をパス名として取得するにはどうすればよいですか?

「本名」とは、を意味/dev/ttyし、同じ端末を参照するために他の任意のプロセスで使用することはできません。可能な場合は、単純なシェルコード(以下の例のような)として、そうでない場合はC関数として、答えを好みます。

これは、標準入力がリダイレクトされても機能する必要があるので、ttyユーティリティを使用できないことに注意してください。標準入力に接続された端末のファイル名を出力するだけなnot a ttyので、このような場合はエラーにttyなります。

Linuxでは、以下を使用できます。

echo "/dev/`ps -p $$ -o tty | tail -n 1`"

しかし、これは移植性がありません。POSIXによると、端末名の形式は指定されていません。

C関数に関してctermid (NULL)/dev/tty、ここでは役に立たないを返します。

注:zshドキュメントによれば、

zsh -c 'echo $TTY'

しかし、現在(バージョン5.0.7)は、標準入力と標準出力の両方がリダイレクトされると失敗します。

$ zsh -c 'echo $TTY > /dev/tty' < /dev/null
/dev/pts/9
$ zsh -c 'echo $TTY > /dev/tty' < /dev/null > /dev/null
/dev/tty

@mikeserv私はpsソリューションがほとんどのシステムをカバーしていると思います(そしてwhoそれ以上は役に立ちませんps)、おそらく識別子だけを処理するための少しだけ多くのコード( "04"など)を備えています。もっとポータブルな解決策があるのか​​と思っていました。
vinc17 2015

これは、事前にペアになっているセット(おそらく古いbsdスタイルのptyペア)にも関係している可能性があります。すべてのptyがUNIX 98タイプであるとは限りません。とにかく、からman xterm-Sccn このオプションは、ことができますxterm既存のプログラムのためのチャンネルO / Iとして使用されるように...オプション値は、スレーブモードでの使用にPTYの名前の数文字、プラス継承されたFDの数です。オプションに「/」文字が含まれている場合は、ftyからpty名を区切ります。
mikeserv 2015

@mikeservソリューションは、GNU / Linuxでも、busybox(Android、BTWで使用される)psからは機能しないことに注意してください。「それを処理できる」とはどういう意味ですか?xterm04
vinc17 2015

busyboxPOSIX準拠ではありません。toyboxただし、非常にうまくいきます。
mikeserv 2015

回答:


8

「管理端末」とも呼ばれます。cttyは、「プロセスが対話している端末」とは異なります。

cttyのパスを取得する標準的な方法はctermid(3)です。これを呼び出すと 、リリース10以降のfreebsdでは実際のパスが検索されますが [1]、古いfreebsdとglibcの実装 [2]は無条件に「/ dev / tty」を返します]。

Linux procps 3.2.8パッケージのps(1)、/ proc / * / stat [3] の数値エントリを読み取り、システムサポートがないため[4、5]を推測してパス名を 部分的に差し引く [6] 。

ただし、cttyに厳密に関心がなく、stdioに関連付けられている端末がある場合、tty(1)はttyname(fileno(stdin))、c と同じであるstdinに接続された端末パスを出力しますreadlink /proc/self/fd/0


無条件の「/ dev / tty」動作に関するそれほど重要ではない考え:仕様は、単純な「現在のパス名である」ではなく、単にctermidによって返された文字列を「パス名として使用した場合、現在の制御端末を参照する」と言います制御端末」。"/ dev / tty"は制御端末ではなく、同じプロセスがopen(3)で制御端末を開いている場合にのみ制御端末を参照すると解釈される場合があります。したがって、「端末は多くても1つのセッションでcttyになる可能性がある」というルールに違反しない [7]。

別の結果として、制御端末がない場合でもctermidは失敗せず、そのような失敗は仕様 [8]で許可されています。そのため、その後のopen(3)が失敗するまで、cttyの不足に気づくことができます。仕様ではopen(3)の呼び出しが成功することは保証されていませんので、これは問題ありません。


psすべてのOSに/procファイルシステムがあるわけではないので、これは私の質問で提供したソリューションよりも移植性が高くありません。それps自体でreadlinkを使用していることに注意してください/proc/self/fd/2(標準エラーがリダイレクトされても機能します)。
vinc17 2015

1
編集。/ proc / * / fd / 2のps readlinkは、cttyを見つけるのではなく、数値の端末をパスにマップするための補足情報を探すために、link [4] [5]を参照してください。
把友情留在無盐

1
素晴らしい編集。cttyについて; 私はvinc17について話すことはできませんが、おそらくどこかにいつでも書き込むことができますが、プロセスグループを存続させるために開いたままにする必要があるファイルは1つだけです。
mikeserv 2015

1
@ vinc17 -あなたが持っている場合は任意のファイル記述子を、あなたがそれらを読むことができますCTTYで開きますttystderrオープンなr / wであることがスペックであるため、おそらく最高です。ですからtty <&2
mikeserv 2015

1
与えられた端末がせいぜい1つのセッションでcttyであるかもしれないということは、glibcがctermid()常に戻ることに対して非準拠になることはありません"/dev/tty"。その名前は常に、それにアクセスするプロセスの制御端末指します。これは、セッションによって異なります。端末はセッション固有ですが、端末にアクセスするための名前である必要はありません。
PellMel 16

5

POSIX仕様は、制御端末が関係する場合の賭けを実際にヘッジし、それによって定義します。

  • 端末の制御
    • 端末を参照している可能性のあるいくつかの特別なファイルのどれが意図されているかという問題は、POSIX.1では扱われていません。パス名/dev/ttyは、プロセスに関連付けられた制御端末の同義語です。

それは定義リストにあります-それがすべてです。しかし、一般的な端末インターフェイスでは、さらにいくつかが言われています:

  • 端末は、その制御端末としてプロセスに属する場合があります。制御端末を持つセッションの各プロセスには、同じ制御端末があります。端末は最大で1つのセッションの制御端末である場合があります。セッションの制御端末は、実装で定義された方法でセッションリーダーによって割り当てられます。セッションリーダーに制御端末がなく、O_NOCTTYオプションを使用せずにセッションに関連付けられていない端末デバイスファイルを開く場合(open()を参照)、端末がセッションの制御端末になるかどうかは実装定義です盟主。

  • 制御端末は、fork()関数呼び出し中に子プロセスによって継承されます。プロセスは、プロセスとの新しいセッションを作成するときに、制御端末を放棄します。setsid()関数; 制御端末としてこの端末を持っていた、古いセッションに残っている他のプロセスは、引き続きそれを持っています。制御端末に関連付けられているシステム内の最後のファイル記述子(現在のセッションにあるかどうかに関係なく)を閉じると、その端末を制御端末として使用していたすべてのプロセスが制御端末を使用しなくなるかどうかは不明です。この方法で制御端末が放棄された後、セッションリーダーが制御端末を再取得できるかどうか、およびその方法は指定されていません。他のプロセスが制御端末を開いたままにしている場合、プロセスは、制御端末に関連付けられているファイル記述子をすべて閉じるだけでは、制御端末を放棄しません。

不特定の部分がたくさん残っています-正直なところ、それは理にかなっていると思います。端末は重要なユーザーインターフェイスですが、実際のハードウェアやプリンターのような場合もありますが、多くの場合xterm、エミュレーターのようなものはほとんどありません。。端末を具体的に説明するのは困難です-とにかく、Unixよりも端末の機能が多いため、Unixの利益に大きく影響するとは思いません。

とにかく、POSIXはps、cttyが関係している場所でどのように動作すべきかについてもかなり不信感を持っています。

-aスイッチがあります:

  • 端末に関連するすべてのプロセスの情報を書き込みます。実装では、このリストからセッションリーダーを省略できます。

すごい。セッションリーダー省略できます。それはあまり役に立ちません。

そして-t

  • termlistで指定された端末に関連するプロセスの情報を書き込みます。アプリケーションは、termlistが<blank>コンマ区切りのリスト形式の単一の引数であることを確認する必要があります。端末識別子は、実装で定義された形式で指定されます。

...これは別の失望です。しかし、XSIシステムについて次のように言っています。

  • XSI準拠のシステムでは、彼らは、次の2つの形式のいずれかで与えられなければならない:デバイスのファイル名(例えば、tty04デバイスのファイル名が始まる場合)、またはtty単に識別子、文字を以下tty (例えば、04

それは少し良いですが、道ではありません。XSIシステムにも-dスイッチがあります。

  • セッションリーダーを除くすべてのプロセスの情報を記述します。

...それは少なくとも明確です。-outputスイッチもttyフォーマット文字列で指定できますが、すでに述べたように、その出力フォーマットは実装定義です。それでも、私はそれが得られるのと同じくらい良いと思います。私は-多くの作業で-上記のスイッチをいくつかの他のユーティリティと組み合わせて使用​​すると、かなり良い球場が得られると思います。しかし、正直に言うと、いつ、どのように動作するかわかりません。また、そのような状況を想像することもできませんでした。しかし、追加fuserfindてパスを確認できれば、と思います。

exec 2<>/dev/null
ctty=$(sh -c 'ps -p "$$" -o tty=' <&2)
sid=$(sh -c 'ps -Ao pid= -o tty=|
      grep '"$ctty$"' | 
      grep -Fv "$(ps -do pid=)"'  <&2)
find / -type c -name "*${ctty##*/}*" \
       -exec fuser -uv {} \; 2>&1  |
grep ".*$ctty.*${sid%%"$ctty"*}"

これ/dev/nullは、検索サブシェルのいずれにも0、1、2のいずれかがcttyに接続されていない場合に機能することを示すためのものです。とにかく、それは印刷します:

/dev/pts/3:          mikeserv   3342 F.... (mikeserv)zsh

さて、上記は私のマシン上の完全なパスを取得しており、ほとんどの場合、ほとんどの人にとってそうなると思います。それが失敗することも想像できます。これは大まかなヒューリスティックです。

これは他の多くの理由で失敗する可能性がありますが、セッションリーダーがすべての記述子をcttyに放棄し、仕様が許す限りsidを維持できるシステムを使用している場合、これは間違いなく役に立ちません。とはいえ、ほとんどの場合、これでかなりの見積もりが得られると思います。

もちろん、最も簡単な事はあなたが持っている場合は行うには任意のあなたのCTTYに接続記述子をちょうどです...

tty <&2

...または類似。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.