コンソールでの便利な作業、またはSTDERRの赤塗り

コンソールで作業する



私たちの多くは毎日コンソールを使用していますが、おそらく誰もが自分自身に質問をしました。コンソールでの作業をより効率的にするにはどうすればよいでしょうか? 日常業務の時間を短縮するために何ができますか? この記事では、bashの操作について、知らないかもしれないいくつかの簡単だが便利なことについて簡単に説明したいと思います。



入力する文字数を減らす



エイリアス


すべての最新のシェルがサポートする最も有用なものの1つはエイリアスです。 エイリアスを使用すると、コマンドを入力するときに書く文字を減らすことができます。 例:



$ git status # On branch master nothing to commit, working directory clean $ alias st='git status' $ st # On branch master nothing to commit, working directory clean
      
      





ご覧のとおり、エイリアスを宣言するための構文は非常に単純であるため、ほとんどすべての人が何らかの方法でエイリアスを使用しています。 エイリアスを使用することの欠点の1つは、チームの自動補完が機能しなくなることです。



例:(<TAB>はTabキーを押しています):



 $ git checkout <TAB><TAB> HEAD master origin/HEAD origin/master
      
      





エイリアスco = 'git checkout'を宣言した場合、Tabを押すと期待どおりに動作を停止し、ファイル名の置換を開始します(少なくともbashの場合):



 $ alias co='git checkout' $ co <TAB><TAB> Display all 124 possibilities? (y or n) .git/ MANIFEST.doc array.c bashline.c ...
      
      







したがって、より少ない文字を書きたい場合は、オートコンプリートを放棄する必要があります...またはそうではありませんか? 少しグーグルして、このような興味深い関数を見つけましょう。



 function make-completion-wrapper () { local function_name="$2" local arg_count=$(($#-3)) local comp_function_name="$1" shift 2 local function=" function $function_name { ((COMP_CWORD+=$arg_count)) COMP_WORDS=( "$@" \${COMP_WORDS[@]:1} ) "$comp_function_name" return 0 }" eval "$function" }
      
      







この興味深い関数により、オートコンプリートを「複雑な」コマンドに戻すことができます(つまり、git checkoutの場合のように、コマンドと追加の引数で構成されるエイリアスを作成する場合)。 これにより、関数を作成し、それを使用して自動補完関数をラップし、エイリアスの自動追加が引き続き機能するようにすることができます。 複雑に聞こえる..? だから:)。 使用例を見てみましょう。



 $ make-completion-wrapper _git _co git checkout $ complete -o bashdefault -o default -o nospace -F _co co $ co <TAB><TAB> HEAD master origin/HEAD origin/master
      
      





もう少し詳しく見てみましょう。



1)make-completion-wrapper:チームの自動補完機能は通常アンダースコアで始まります。たとえば、gitコマンドの場合、この機能は「_git」と呼ばれます。 すばらしいmake-completion-wrapper関数を使用して、「git checkout」のエイリアスである「_co」(coコマンド用)という新しい関数を作成します。

2)完了:新しい関数「_co」を「co」コマンドの自動補完のハンドラーとして登録します

3)動作します!



エイリアスと自動補完コマンドが失われないように、これらのコマンドをホームディレクトリの「.bashrc」(または「.bash_profile」または「.profile」)に保存します。



機能


bashのエイリアスの機能は限られているため、関数を書くと便利な場合があります(前の例のように)。 bashの関数は別のコマンドであるかのように機能しますが、同時に現在のシェルと同じコンテキストで実行されます。 つまり、シェルスクリプトを記述しているかのように、引数は「$ 1」、「$ 2」などとして関数に渡されます。 「引用符で囲まれた」略語「$ @」も機能し、すべての引数を「そのまま」正しい場所に置き換えます。 serverfaultでは、指定されたコマンドに対してstderrを赤で表示するような関数の例を見つけることができます



 $ color()(set -o pipefail;"$@" 2>&1>&3|sed $'s,.*,\e[31m&\e[m,'>&2)3>&1 $ color ls nonexistent ls: nonexistent: No such file or directory #   
      
      





上記のようなコマンドを作成しなくても、たとえば必要な引数を常にコマンドの最後に追加する必要がある場合は、関数を使用すると便利です。



 $ function echoworld () { echo "$@" world; } $ echoworld Hello Hello world
      
      





または、引数に対していくつかの簡単な操作を行う必要がある場合:



 $ function gmo () { git merge "origin/$1"; } $ gmo master # git merge "origin/master" Already up-to-date.
      
      





moshを使用してSSH入力の遅延を取り除く



非常にリモートのサーバー(アメリカのAmazonのAmazonクラウドなど)でSSHを頻繁に使用する必要がある場合、またはモバイルインターネット経由でSSHを使用する場合、入力遅延の問題に慣れています。 つまり、文字を入力すると、200ミリ秒以上になることがあるラウンドトリップインターバルの後にのみ、相手側に表示されます。 モバイルインターネットの場合、入力の遅延ははるかに強く感じられ、作業はすでに完全に不快になっています。



おそらく、mosh( http://mosh.mit.edu )というユーティリティの作成者はこの問題にうんざりしていたため、UDPの上で動作する独自のSSH置換を記述し、多くのSSHの問題を解決することを決めました。接続(書き込みに失敗しました:何かを入力しようとしたときにのみ表示される破損したパイプ)。



このユーティリティには、重大な欠点も1つあります。現時点では、履歴の表示はサポートされていません。 つまり、大きなファイルからcatを作成した場合、または大きなディレクトリからlsを作成した場合、出力の最後の行のみが取得され、先頭が「失われ」ます。 著者自身は現在、リモート側の画面を使用してこの問題を解決することを推奨しており、バージョン1.3では、同様の機能をサーバー自体(およびクライアント)に直接埋め込むことを約束しています。



stderrが赤になるようにパッチbash



実際、赤いstderrを取得するためにbashにパッチを当てる必要はまったくありませんが、興味深いことです! グーグルで簡単に作成できる既製のソリューションがあります。たとえば、これはgithub.com/sickill/stderredです。 このプロジェクトは、libcからの書き込み(2、...)およびfprintfの呼び出しをインターセプトし、赤色を取得するために必要なescシーケンスのラッパーを追加する共有ライブラリです。



それで、他の解決策が存在すること、そしてそれらがすべての人に合っていることに気づいたので、とにかく私たち自身で書きましょう:)! 私はすぐに、パッチを実装する際に支援してくれたezhに別の感謝を申し上げます。



1. bashソースのダウンロード( ftp.gnu.org/gnu/bash

2. gitに追加します(git init && git add -A && git commit -m 'Initial commit')

3. bashのビルド(./configure && make)

4. bashを実行し、すべてが機能することを確認します(./bash -l)

5.ソースコードの理解を開始します。



shell.cファイルを見つけて、bashの初期化が始まる場所を確認します。



 #if defined (NO_MAIN_ENV_ARG) /* systems without third argument to main() */ int main (argc, argv) int argc; char **argv; #else /* !NO_MAIN_ENV_ARG */ int main (argc, argv, env) int argc; char **argv, **env; #endif /* !NO_MAIN_ENV_ARG */ {
      
      







約400行後、まだmain()関数にいる間に、最後にreader_loop()呼び出しが見つかります:



 #if !defined (ONESHOT) read_and_execute: #endif /* !ONESHOT */ shell_initialized = 1; /* Read commands until exit condition. */ reader_loop (); exit_shell (last_command_exit_value); }
      
      







必要な環境変数が次の場合、bashがユーザー入力の読み取りを開始する直前にくさびを入れ、何らかの方法で番号2(stderr)のハンドルをインターセプトすることは論理的です。



  shell_initialized = 1; color_stderr = get_string_value("COLOR_STDERR"); if (color_stderr && color_stderr[0]) { init_color_stderr(); } /* Read commands until exit condition. */ reader_loop ();
      
      







番号2のハンドルをインターセプトする方法は? 明らかな解決策は、パイプを作成し、記述子を番号2のパイプに置き換え、そこから読み取られた別のスレッドで、必要なescシーケンスを追加することです。



 static void *colorize_stderr(void *void_thread_args) { struct stderr_thread_data* data = (struct stderr_thread_data*)void_thread_args; int n; char buf[1024]; #define STDERR_PREFIX "\033[31m" #define STDERR_SUFFIX "\033[m" for (;;) { n = read(data->pipe, buf, sizeof(buf)); if (n <= 0) { if (errno == EINTR) continue; pthread_exit(NULL); } write(data->err, STDERR_PREFIX, sizeof(STDERR_PREFIX) - 1); write(data->err, buf, (size_t) n); write(data->err, STDERR_SUFFIX, sizeof(STDERR_SUFFIX) - 1); } } static void init_color_stderr () { pthread_t thr; int pipes[2]; static struct stderr_thread_data data; pipe(pipes); data.err = dup(2); dup2(pipes[1], 2); data.pipe = pipes[0]; pthread_create(&thr, NULL, colorize_stderr, (void*) &data); }
      
      







しかし、すべてのユーザー入力も赤になります:(。どうやら、readlineライブラリーは入力をstderrだけで画面に表示します... readlineライブラリーに突っ込んで、rl_redisplay関数をlib / readline / display.cファイルに挿入します(ちなみに、この関数は1300行で)以下:



 /* Basic redisplay algorithm. */ void rl_redisplay () { /* ...   ... */ if (_rl_echoing_p == 0) return; _rl_output_some_chars("\033[m", 3); /*  -:  ,      ,    */ /* Block keyboard interrupts because this function manipulates global data structures. */ _rl_block_sigint (); RL_SETSTATE (RL_STATE_REDISPLAYING);
      
      







すべてが正しく行われ、必要なメソッドヘッダーとシグネチャが追加された場合(簡潔にするためにこれらのアクションは省略されました)、. bashrcに「export COLOR_STDERR = 1」という行を記述し、新しいコンパイル済みbashを実行すると、stderr全体が最初のスクリーンショットのように赤になります記事。



システムbashを置き換えることは悪い考えなので、新しいコンパイル済みbashを、たとえば〜/ bashに入れて、次を.bashrcに追加できます。



 if [ ! -z "$PS1" ] && [ -z "$MY_BASH" ] && [ -x ~/bash ]; then export MY_BASH=1 exec ~/bash -l "$@" fi export COLOR_STDERR=1
      
      







ログインは、「〜/ bash」が存在するかどうか、実行可能かどうかを確認し、存在する場合は、現在のプロセスを「〜/ bash -l」(つまり、ログインシェル)に置き換えます。 オプションCOLOR_STDERR = 1は、bashのstderrを赤で表示します。



bashのパッチバージョンはgithubで入手できます: github.com/YuriyNasretdinov/bash



編集はbashの「松葉杖」の形で行われるため、このパッチがメインブランチに受け入れられることはほとんどありませんが、実装自体はかなりおもしろそうです。実際、仕事では、1つの環境変数のみを設定する必要がありますコードから、このモードはデフォルトでオンになります)、他のすべては変更なしで動作します。



読者の皆様、この記事から何か役に立つことを学べることを願っています。 明けましておめでとうございます!



All Articles