Fish-shell: 機能するパイプを読み取れません

作成日 2012年07月05日  ·  21コメント  ·  ソース: fish-shell/fish-shell

テスト:

function testfun
    set input (cat)
    echo $input
end

echo testing123 | testfun

これは「testing123」を出力するはずですが、何も生成しません。

それはbashで完全に機能します:

function testfun
{
    input="$(cat)"
    echo $input
}

echo testing123 | testfun
bug

最も参考になるコメント

この問題は、 grepのデフォルトのgrep関数と相まって、かなりの数の問題を引き起こします-この問題がすぐに修正されない場合は、デフォルトのgrepエイリアスを削除する(または置き換える)必要があります少なくとも発生を最小限に抑えるための略語、多分?)。

@milieuが示唆しているようにcat使用しても、Fish 2.3.1では問題が解決しないようです(少し遅れていることに気づきましたが、Fedora 25用にパッケージ化されたバージョンです)

全てのコメント21件

回避策として「読み取り」機能を使用できます。

function t
    while read -l line
        echo $line
    end
end

stdinが「cat」ではなく「set」にパイプされているため、魚では機能しないことを理解しました。

この問題は、特定の方法で機能するように「設定」するだけではありません。 関数への配管はまったく失敗します。 また、関数に送信されたターミナルI / Oは機能しますが、それでも少し奇妙です。入力をバッファリングして一度に配信するように見えます。 この関数で何が起こるかを観察します。

~> function meh
       cat
   end
~> # First, the way it's supposed to work.
~> # As input, we press the keys: a RET b RET control-D
~> cat
a
a
b
b
~> cat | cat
a
a
b
b
~> # Now...
~> meh
a
a
b
b
~> # So far so good, but...
~> cat | meh
a
b
^D
... um...
^D
control-D repeatedly does not work
try control-C
Job 1, “cat | meh” has stopped
~> fg
Send job 1, “cat | meh” to foreground
cat: stdin: Interrupted system call
~> jobs
jobs: There are no jobs
~> # Dear lord.
~> # For completeness...
~> meh | cat
a
b
aD
b
~> 

また、 cat | meh | catは、 cat | begin; cat; endと同じように動作します。
さらに、 cat | mehシステムコールが中断されたことを訴える「猫」が最初の「猫」であることがわかります。 あれは:

~> cp /bin/cat mycat
~> ./mycat | meh
Job 1, “./mycat | meh” has stopped  #after control-C
~> fg
Send job 1, “./mycat | meh” to foreground
mycat: stdin: Interrupted system call

だからそれがあります。 明らかに、これは魚が関数を呼び出す方法と、魚が関数にパイプを構築する方法と関係があります。 誰かがこれについて知っていますか?

わかりました、私はその実行を見つけています
pbpaste | begin; cat; end
クリップボードが「23 \ n」の新鮮な魚の殻で繰り返し、23を印刷して戻すことがあり、殻がロックされることがあります。その時点で、control-Cは何もできません。 これはある種の競合状態に違いないと思います。 ああ少年。

一方、シグナルSIGTTINが./mycat | begin; cat; endの「mycat」に送信されているようです。

     21    SIGTTIN      stop process         background read attempted from
                                             control terminal

次に、GNU libcマニュアルによると、「プロセスは、バックグラウンドジョブとして実行されている間、ユーザーの端末から読み取ることはできません。バックグラウンドジョブのいずれかのプロセスが端末から読み取ろうとすると、ジョブ内のすべてのプロセスが送信されます。 SIGTTINシグナル。」

つまり、「mycat」はバックグラウンドで開始されるか、開始されてからバックグラウンドに配置され、魚の機能のようなものにパイプされるように見えます。 おそらく、この知識が役立つでしょう。

これは明らかにパイプの両側を背景にします...しかし、 fgコマンドを与えると、プロセスが背景から引き出され、想定どおりに機能するようになります。

~ $ alias pjson='python -m json.tool | pygmentize -l json'
~ $ curl -u smoku -X GET -H "Content-Type: application/json" 'https://jira.......' | pjson
Job 4, 'curl -u smoku -X GET…' has stopped
~ $ fg
Enter host password for user 'smoku': ********
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   593    0   593    0     0   1372      0 --:--:-- --:--:-- --:--:--  1375
~ $ fg
{
    "expand": "renderedFields,names,schema,transitions,operations,editmeta,changelog",
    "id": "29874"
}

単純なエイリアスではなく、 $PATH pjsonラッパースクリプトを作成する必要があるのは少し面倒です... :(

私の参考のために、これはopenSUSEのバグhttps://bugzilla.opensuse.org/show_bug.cgi?id=963548でもあり

わーい! 私は自分の回避策を見つけたと思います! 魚の配管構文を説明する第110号のgustafjのコメントのおかげで、私はこれを思いついた:

function line --argument-names n
    cat 1>| tail -n +$n | head -1
end

この問題は、 grepのデフォルトのgrep関数と相まって、かなりの数の問題を引き起こします-この問題がすぐに修正されない場合は、デフォルトのgrepエイリアスを削除する(または置き換える)必要があります少なくとも発生を最小限に抑えるための略語、多分?)。

@milieuが示唆しているようにcat使用しても、Fish 2.3.1では問題が解決しないようです(少し遅れていることに気づきましたが、Fedora 25用にパッケージ化されたバージョンです)

シェル内での実行とコマンドラインからの実行には違いがあるようです。

(zsh)$ ./fish -c "read n | grep nothing"    
read> lol
(zsh)$ ./fish
(fish)$ read n | grep nothing
read> 
# Stuck forever, needs to kill the terminal. ^C, ^Z have no impact.

多分これは問題のデバッグに役立ちますか?

@layus :いいえ、それは#3805です。これは、魚自体がターミナルを制御できないという問題です。

_この点での元の動作の一部が変更されたと思います。以下はフィッシュマスター/3.0に関するものです_

ここで魚が間違っている2つの基本的な問題があります。1つは関数/ブロック出力のバッファリング(最新のシェルはどこにも何もバッファリングしないと確信しています)、2つ目はブロック全体で入力/出力を正しくチェーンできないことです。 いくつかのコーナーケースで正しい行動がどのように見えるべきかについては多くのあいまいさ(または少なくとも許容できる意見の違いの余地)がありますが、魚が現在最適であると主張する人はいないと思います。

一般に、簡単な外部コマンド(および全体として同じように効果的に処理される組み込みコマンド)があります。1つの入力と2つの出力で、一方は後続のコマンドにチェーンでき、もう一方はリダイレクトする必要がありますファイルまたはtty。 ただし、ブロックと関数は、基本的に入力(1つしか存在できないため)を一連の(最終的に拡張される)外部コマンドまたは組み込み関数にマッピングするため、注意が必要です。

そうは言っても、私は現在の行動が間違っていることに同意しません。 (cat)は、実行されるコマンドにパイプされたデータを読み取ってはなりません。

mqudsi<strong i="11">@ZBook</strong> /m/c/U/m/Documents> type testfun
testfun is a function with definition
function testfun
    set input (cat)
    printf "You said '%s'\n" $input
end
mqudsi<strong i="12">@ZBook</strong> ~/r/fish-shell> echo testing123 | testfun
hello
^D
You said 'hello'

セットが入力を消費するか、入力の一部を消費するか、入力を完全に無視するかに関係なく、入力をブロックにパイプします。catは入力用に/dev/ttyに接続するのが正しいので、正しく渡されます。コマンドラインに置き換えるためのシェル。 実際、このリポジトリに対して、あるレベルの間接参照で実行されたときに「サブシェル」が端末から読み取られなかった場合について不平を言うバグが(多く)提出されました。 私見ですが、ここで壊れているのはbashです。特に、bashは実際のサブシェルをサポートし、ここで非同期性を提供するためです。

私が言う唯一の壊れた振る舞いは、外部コマンドが関数/ブロックで起動され、入力を完全に消費しない場合に起因します。

mqudsi<strong i="19">@ZBook</strong> /m/c/U/m/r/fish-shell> printf 'foo\nbar\n' | begin
                                        head -n1 | read -l line1
                                        head -n2 | read -l line2
                                        echo line1: $line1
                                        echo line2: $line2
                                    end
line1: foo
line2:

TBH私は非常に驚いていますが、これは正しく機能します。

mqudsi<strong i="23">@ZBook</strong> /m/c/U/m/r/fish-shell> printf 'foo\nbar\n' | begin
                                        /bin/echo 'hi from echo'
                                        cat | read -z from_cat
                                        printf 'from_cat: "%s"' $from_cat
                                    end
hi from echo
from_cat: "foo
bar
"¶  

そしてこれも正しいです:

mqudsi<strong i="27">@ZBook</strong> /m/c/U/m/r/fish-shell> printf 'foo\nbar\n' | begin
                                        cat | read -zl from_cat1
                                        cat | read -zl from_cat2
                                        printf 'from_cat1: "%s"\n' $from_cat1
                                        printf 'from_cat2: "%s"\n' $from_cat2
                                    end
from_cat1: "foo
bar
"
from_cat2: ""

特に、非同期実行で魚に実際のサブシェルを導入する計画を考慮すると、ここで報告された元のケースに関する魚の行動は正しいと言えます。 実際、誰かが反対し、ここで説得力のある議論をすることができない限り、私はこの問題を完全に終わらせる傾向があります。

元のバグレポートは無効ですが、 @ waterhouseによって提起された問題は、cat | mehケースを含め、#5219がそれらを修正しているように見えることです。

mqudsi<strong i="8">@ZBook</strong> ~/r/fish-shell> cat | meh
a
a
b
b
^D
mqudsi<strong i="9">@ZBook</strong> ~/r/fish-shell>

そうは言っても、私は現在の行動が間違っていることに同意しません。 (cat)は、実行されるコマンドにパイプされたデータを読み取ってはなりません。

私はそれについて非常に同意しません!

catは入力のために/ dev / ttyに接続するのが正しい

それはメンタルモデルの問題です。 catは入力のために「現在のstdin」に接続していると言えます。 関数またはブロックがリダイレクトされない場合、それはttyです。 リダイレクトされた場合、それだけです! したがって、ここで/ dev / ttyに接続するのは正しくありません。

いくつかのレベルの間接参照で実行されたときに「サブシェル」が端末から読み取られなかった場合について不平を言う

これらはすべて「グローバル」コマンド置換に関するものであることに注意してください。 たとえば、コマンドラインでecho (fzf)を実行します。 その場合、stdinはありません。

したがって、私が言うことは、次のように機能します。

echo | echo (cat) # from tty

begin
   echo | echo (cat) # from file
end < file

この場合、stderrについて尋ねる関連する問題(#1035)があり、それはリダイレクトされません。 これは、古いmath関数のかなりの問題でした。これは、関数内にコマンド置換が含まれているため、リダイレクトできなかったためです。

これはその標準部分です。 関数が裸の(cat)実行する場合、それを常にttyから読み取ることは本当に便利ですか? または、その場合は</dev/ttyだけを使用できませんか?

面白い考え。

かっこが単純な置換を示しているか(つまり、「かっこの内容が上の行にあると偽って、実行して完了し、結果を変数に格納して、ここで変数を置換する」)、またはそれらが 'であるかどうかに要約すると思います。 re(現在壊れている)サブシェル。 魚には適切なサブシェルサポートがないというコンセンサスがあると思いましたが、その意図は常に「ある時点で」それを修正することでした。

前者の場合は、はい、同意します。括弧の内容を別の行に移動すると、ブロックにリダイレクトされる入力から確実に読み取る必要があるため、現在の動作は壊れています。

しかし、サブシェルはそれよりもそこでのメンタルモデルと互換性がないと思います。

括弧が単純な置換を示しているか(つまり、「括弧の内容が上の行にあるように見せかけ、それらを最後まで実行し、結果を変数に格納し、ここで変数を置換する」)、またはそれらが(現在壊れている)サブシェルであるかどうか。

これらの用語は、ここであまり役に立たないほど明確に定義されているとは思いません。

私にとって、それはより自然で、より典型的で、より有用なものに帰着します。

ターミナルからの読み取りは確かに便利であり、別のstdinがある場合でもターミナルから読み取りたい場合があります(たとえば、 fzfは基本的にこれだけを実行します)。

しかし、特に非対話型の使用ではttyからまったく読み取られないことを考えると、stdinからの読み取りははるかに一般的だと思います。 そして、ttyからの読み取りは(その</dev/ttyリダイレクトを介して)まだ可能であるため、それを2番目のオプションとして残しておくのは問題ないようです。

私が提案しているモデルに</dev/ttyの反対がないという事実は、私に自分の立場を再考させています。

私は議論を完全に理解するのに十分なほどシェルに深く入っていないかもしれません。 しかし、私は何かを解決する必要があり、それを解決するためにbashスクリプトが必要かどうか疑問に思っています。

これは基本的に非常に単純なタスクです。stdoutを(z)catからpvを介してmysql cliにパイプします(基本的にバックアップを復元するため)。接続文字列を入力したくないので、そのための関数を使用します。 :

function mysqlenv --description connect to mysql server using config from .env
  mysql -u (getEnv DB_USERNAME) -p(getEnv DB_PASSWORD) (getEnv DB_DATABASE)
end

最初は、コマンドのstdinが左側のコマンドからstdoutであることは明らかであるため、これが機能すると確信していましたが、今は混乱しています。 mysqlenvはコマンドではなく、関数です。 今、私はここでたくさんのテキストとたくさんの「これはうまくいくはずです」を読んでいますが、何も機能していません。

私が試したこと:

  • cat -|mysql...出力なし。 mysqlは入力を取得しません。 ctr + cはmysqlに存在します。 パイプはバックグラウンドで実行されています
  • mysql... <&0出力なし。 mysqlは入力を取得しません。 ctr + cはmysqlに存在します。 パイプはバックグラウンドで実行されています
  • set input (cat); mysql...出力なし。 mysqlは入力を取得しません。 ctr + cはすべて存在します。 バックグラウンドには何も残っていません
  • read -z|mysql...出力なし。 mysqlは入力を取得しません。 ctr + cは^c

再び私のコマンドプロンプト: zcat some_backup.sql.gz|pv -s (zsize some_backup.sql.gz)|mysqlenv 。 mysqlで直接使用した場合(間にfish関数を使用しない場合)のパイプステータスが表示されるため、機能するはずです。

では、関数から関数内のコマンドのstdinにstdinを与える方法を教えてください。

while read...介してすべての回線に再接続する必要があるとは言わないでください。 動作する可能性がありますが、処理が遅すぎるため、解決策ではありません。

@tflori :はるかに簡単です。 リダイレクトせずに、コマンドをそのままにしておきます。 問題は、関数内のコマンドに直接関係しているわけではありません。 何かのようなもの

function foo
    cat
end

動作します。 catは、本来のようにstdinを取得します。

それがコマンド置換にあるとき、それはあなたがこのバグにぶつかっているときではありません。

@fahoは、初期関数が機能する必要があることを意味しますか? しかし、そうではありません。 多分私のバージョンは古くなっていますか? 現在2.7.1を使用しています

多分私のバージョンは古くなっていますか? 現在2.7.1を使用しています

@tflori :はい、3.0.2が必要です。

このページは役に立ちましたか?
0 / 5 - 0 評価