* 1つのコマンドを別のコマンドの引数として渡すのが間違った設計であるのはなぜですか?

* 1つのコマンドを別のコマンドの引数として渡すのが間違った設計であるのはなぜですか?

出力が端末に収まらない場合は、ポケットベルに自動的にパイプするようにコマンドをラップできます。

現在私は次のシェル機能を使用しています(Arch Linuxのzshで):

export LESS="-R"

RET="$($@)"
RET_LINES="$(echo "${RET}" | wc -l)"

if [[ $RET_LINES -ge $LINES ]]; then
  echo "${RET}" | ${PAGER:="less"}
else
  echo "${RET}"
fi

しかし、それは私にあまり説得力がありません。私が望むことを達成するためのより良い方法(堅牢性とオーバーヘッドの面で)はありますか?うまくいけば、zsh固有のコードでも開いています。


修正する:この質問をしてから知った事実は回答$LINESlessすべての出力をキャッシュするのではなく、送信する前にほとんどのラインをバッファリングするより良い(より複雑な場合)ソリューションを提供します。残念ながら、どちらのソリューションも長く複雑な問題を解決できないため、これも満足のいくものではありません。たとえば、上記のコードが次の関数に格納されているpager_wrap場合

pager_wrap echo {1..10000}

ページャを介してパイプするのではなく、長い行を標準出力として印刷します。

ベストアンサー1

POSIXシェル準拠のために書かれたソリューションがありますが、bashでのみテストしたため、移植可能かどうかはわかりません。そして私はzshを知らなかったので、zshにやさしくすることを試みませんでした。コマンドを他のコマンドに引数として渡すのは誤った設計です

もちろん、この問題を解決するには、端末の行数と列数を知る必要があります。以下のコードは、依存関係LINESCOLUMNS環境変数があると仮定しています(less参照)。より信頼できる方法は次のとおりです。

  • rows="${LINES:=$(tput lines)}" 次のcols="${COLUMNS:=$(tput cols)}"ように使用してください。APおすすめまたは
  • 出力を表示しますstty size。このコマンドは端末を標準入力として使用する必要があるため、スクリプト内にあり、スクリプトにパイプする場合stty size <&1(bashから)またはと言う必要がありますstty size < /dev/tty。出力をキャプチャするのははるかに複雑です。

秘密要素:このfoldコマンドは、画面のように長い行を分割し、スクリプトが長い行を正しく処理できるようにします。

#!/bin/sh
buffer=$(mktemp)
rows="$LINES"
cols="$COLUMNS"
while true
do
      IFS= read -r some_data
      e=$?        # 1 if EOF, 0 if normal, successful read.
      printf "%s" "$some_data" >> "$buffer"
      if [ "$e" = 0 ]
      then
            printf "\n" >> "$buffer"
      fi
      if [ $(fold -w"$cols" "$buffer" | wc -l) -lt "$rows" ]
      then
            if [ "$e" != 0 ]
            then
                  cat "$buffer"
            else
                  continue
            fi
      else
            if [ "$e" != 0 ]
            then
                  "${PAGER:="less"}" < "$buffer"
                  # The above is equivalent to
                  # cat "$buffer"   | "${PAGER:="less"}"
                  # … but that’s a UUOC.
            else
                  cat "$buffer" - | "${PAGER:="less"}"
            fi
      fi
      break
done
rm "$buffer"

これを使用するには:

  • 上記の内容をファイルに入れてくださいmypager
  • (オプション)これを検索パスのディレクトリに配置します$HOME/bin
  • と入力して実行可能にしますchmod +x mypager
  • ps ax | mypagerまたは同じコマンドに使用してくださいls -la | mypager
    2番目のステップ(検索パスのディレクトリにスクリプトを置く)をスキップした場合は、これを行う必要があります。ps ax | path_to_mypager/mypagerpath_to_mypager「などの相対パスである可能性があります。.」。

* 1つのコマンドを別のコマンドの引数として渡すのが間違った設計であるのはなぜですか?

1.美学/伝統の固守/Unix哲学

Unixには哲学がある~の何かをしてうまくいく。たとえば、プログラムが特定の方法(ページャのように)でデータを表示する場合は、データを生成するメカニズムを呼び出すべきではありません。これがパイプの目的です。

ユーザーが指定したコマンドやプロシージャを実行するUnixプログラムはあまりありません。これを行ういくつかを見てみましょう。

  • Shellは、OKと同様に、ユーザーが指定したコマンドを実行するシェルです。sh -c "command"
    働く;これは一つシェルがすること。 (もちろん、シェルが単純なプログラムであるという意味ではありません。)
  • env、、、、、、そして。nicenohup​​これらのプログラムには共通点があります。つまり、すべて変更された実行環境でプログラムを実行するために存在するということです Unixは通常、他のプロセスの実行環境を変更することを許可していないため、自分のやり方で動作する必要があります。次に、および/または。 _______ 1  私はこの表現を使用していますsetsidsusudoforkexec

    実行環境 広く言うと、環境変数を意味するだけでなく、nice値、UIDとGID、プロセスグループ、セッションID、制御端末、開かれたファイル、作業ディレクトリ、umask値、ulimits、信号処理、alarmタイマー、その他のプロセス属性も含みます。など。
  • 「シェル脱出」を可能にするプログラム。心の中に浮かぶ唯一の例はvi/ですvim。もちろん、他の例もあると確信しています。歴史的な遺物です。これは、ウィンドウシステムまたはジョブ制御の前にあります。ファイルを編集して他の操作(ディレクトリリストの表示など)を実行するには、シェルに戻る前にファイルを保存してエディタを終了する必要があります。別のウィンドウに切り替えるか、Ctrl+ Z(または入力:suspend)を使用してエディタをアクティブに保ちながらシェルに戻ることができるため、シェルエスケープは廃止されました。

複製する代わりに、機能を利用するために他の(ハードコードされた)プログラムを実行するプログラムは含まれません。たとえば、いくつかのプログラムは、diffまたはsort。 (たとえば、初期バージョンを使用して文書に使用されている単語のリストを取得し、そのリストを辞書の単語spell リストと比較し、文書内のどの単語が辞書にないかを識別したという話があります。)sort -udiffcomm

2.時間の問題

スクリプトの作成方法によっては、その行RET="$($@)"は呼び出されたコマンドが完了するまで完了しません。したがって、データを生成したコマンドが完了するまで、スクリプトはデータの表示を開始できません。おそらくこの問題を解決する最も簡単な方法は、データ生成コマンドをデータ表示プログラムから切り離すことです(他の方法もあります)。

三。コマンド履歴

  1. 出力が表示フィルタによって処理されるいくつかのコマンドを実行し、出力を確認した後、その出力をファイルに保存することを決定したとします。入力した場合(仮説の例)

    ps ax | mypager
    

    それから入ることができます。

    !:1 > myfile
    

    または、該当する行をタップして編集します。今入力すると

    mypager "ps ax"
    

    それでも戻ってコマンドを編集できますが、ps ax > myfileそれほど単純ではありません。

  2. または、ランニングを次のステップに決定したとしますps uax。すでに入力している場合は、ps ax | mypager次のことができます。

    !:0 u!:*
    

    繰り返しますが、mypager "ps ax"まだ実行可能ですが、間違いなくより難しいです。

  3. ps ax | mypagerまた、2つのコマンドとを見てくださいmypager "ps ax"history1時間後にリストを実行するとします。 ISTMのmypager "ps ax"場合は、どのコマンドが実行されているかを慎重に検討する必要があります。

4. 複雑なコマンド/参照

  1. echo {1..10000}明らかに例のコマンドに過ぎず、 ps axあまり良くありません。ただしたいことがあればどうしますか?小さいもっと現実的ですかps ax | grep oracle?入力したら

    mypager ps ax | grep oracle
    

    実行され、mypager ps ax 出力がパイプされますgrep oracle。したがって、fromの出力長ps axが30行の場合、fromの出力長は3行に過ぎても呼び出されますmypager。これがより劇的な方法で失敗することがあります。lessps ax | grep oracle

    したがって、以前に示したように実行する必要があります。

    mypager "ps ax | grep oracle"
    

    ただし、RET="$($@)"これを処理できません。もちろん、これを処理する方法もありますが、お勧めできません。

  2. キャプチャする出力のコマンドラインがより複雑な場合はどうなりますか?例えば、

    コマンド1アルギニン1」|   コマンド2  'アルギニン2'$'アルギニン3'

    パラメータにはスペース、タブ、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、$​、、、、、、、、、、、、、、、、、、、、、、、、、、そしておそらく混乱した組み合わせです。このようなコマンドは、シェルに直接正しく入力するのが難しい場合があります。今やるべき悪夢を想像してください。|\<>*;&[]()`'"引用するにパラメータとして渡しますmypager

おすすめ記事