xargsの例に関するシェルでの関数型プログラミング

要約 :シェルでリストを迅速かつ美しく処理する方法についてのストーリー、xargsに関する小さなマニュアル、およびプログラミングまたは管理の哲学に関する多くの水。



少しのSEO最適化:カリー化、ラムダ関数、関数の構成、マップ、リストのフィルター処理、シェル内のセットの操作。







システム管理者は、1つのプログラムの出力を取得し、各出力要素に異なるプログラムを適用する必要がある状況に陥ることがよくあります。 または、1つでもありません。 おもしろい(そして役に立たない)例として、次のことを考えます。現在システムで実行されているすべての実行可能ファイルと、それらが使用するすべての動的ライブラリの合計サイズを計算する必要があります。



これは実際の「タスク」ではなく、これはケーススタディであり、これを解決します(ソリューションでは1行になります)。非常に珍しく強力なシステム管理ツールである線形関数プログラミングについて説明します。 パイプ「|」を使用しているため、線形です これは線形プログラミングであり、xargsを使用すると、ネストされたループを持つ複雑なプログラムを1行の機能ビューに変換できます。 この記事の目的は、「ライブラリーのサイズを見つける方法」を示すことではなく、xargsの議論を語ることではなく、ソリューションの精神を説明し、その背後にある哲学を説明することです。



歌詞



プログラミングスタイルはいくつかあります。 それらの1つは次のようになります:リストの各要素について、リストの各要素について、空の文字列でない場合はファイル名を取得し、ファイルサイズがゼロに等しくない場合はカウンターに追加するサイクルを作成します。 ああ、はい、最初にカウンターをゼロに設定する必要があります。



もう1つは次のようになります。

リストの各要素に適用される関数をリストに適用します。この要素が空でない文字列で、この名前のファイルサイズがnullでない場合は、合計に加算します。



言葉でさえ、2番目のオプションが短いことを示しています。



プログラミング言語の表現手段を考慮に入れると、ループ、サイクルからの不正な終了など、通常のサイクルの問題のない、容量のある構造が得られます。



実際、議論中の問題の文脈では、リストの各要素に適用する必要がある関数を示すことについて話している。 関数自体は、独自の処理関数を持つリストハンドラーになることもあります。



決定データ



問題を解決するには、実行中のプロセスのリストを取得する必要があります。 これは思ったより簡単です-すべてのプロセスは/ proc / [0-9] +にあります。 次に、バイナリへのパスが必要です。 これも簡単です。/proc/ PID /核プロセスを除くすべてのプロセスのexeは、プロセスへのパスを示します。 次のタスク:ファイルのライブラリのリストが必要です。 これは、ファイルパスを予期し、ライブラリのリストを(トリッキーな形式で)表示するlddコマンドによって実行されます。 次の質問は、テクノロジーとシンボリックリンクの歩き方です。ライブラリのシンボリックリンクを最後まで調べてから、各ファイルのサイズを計算する必要があります。



したがって、タスクの高レベルの説明は次のようになります。実行可能ファイルとライブラリのリストを取得し、それぞれのサイズを確認します。



このプロセスでは、実行可能ファイルのリストが2回使用され、1回は「単独で」、2回目は各ファイルのライブラリのリストを取得することに注意してください。



命令的な決定



(詳細を誇張して省略してください)

get_exe_list(){ for a in `ls /proc/*; do readlink -f $a/exe; done } get_lib(){ for a in `cat `; do ldd $a done |awk '{print $3}' } calc(){ sum=0 sum=$(( $sum + `for a in $(cat); do du -b $a|awk '{print $1}'; done` )) } exe_list=`get_exe_list|sort -u` lib_list=`for a in $exe_list; do get_lib $a;done|sort -u` size=$(( calc_size $exe_list + calc_size $lib_list)) echo $size
      
      







うんざりだよね? これは、pidの正しいフィルタリングのための正規表現なしで(存在しない/ proc / mdstat / exeを読み取る必要はありません)、多くのエラーを処理することなく注意します。



リスト



タスクを認識しています。 入力データは同種のファイルであるため、単純にリストとして提示し、同じ方法で処理できます。 Capsule私はコードの「書かれていない」セクションを書きます。



パート1:ダブルリスト処理





少しカンニングし、stderrを使用してリストを複製します。



 (EXE_LIST |tee /dev/stderr|LIB_LIST) 2>&1 | CALC
      
      







このコードは何をしますか? EXE_LISTの書き込まれていない部分は、システムにexeリストを生成します。 teeはこのリストを取り、stderr(ストリーム番号2)に書き込み、stdout(ストリーム番号1)に書き込みます。 スレッド#1はLIB_LISTに渡されます。 次に、3つすべてのコマンド(ブラケット)の出力をstderrと組み合わせ、単一のリストとしてstdoutに入れて、CALCに渡します。



ここで、EXE_LISTを実行する必要があります



パート2:リストフィルタリング





(システム内の_all_プロセスを見るには、rootになる必要があります)。



少し変わった方法で進み、ループ内のlsの代わりに、findを使用します。 原則としてlsも可能ですが、シンボリックリンクの処理にはさらに問題があります。



したがって: find /proc/ -maxdepth 2 -name "exe" -ls





スレッドを無視するにはmaxdepthが必要です。lsの出力と同様の結論が得られます。 除外する必要があります。



そこで、EXE_LISTを改善します。



 find /proc/ -name "exe" -ls 2>/dev/null|awk '{print $13}'
      
      





観察:stderrを使用してデータを送信するため、あらゆる種類の核スレッドに関する問題についてフラッドファインドを検出する必要はありません。



パート3:LIB_LIST





簡単です:転送された各ファイルに対して、lddを使用し、出力をとかす必要があります。



 xargs -n 32 -P 4 ldd 2>/dev/null|grep "/"|awk '{print $3}'
      
      







何がそんなに面白いの? 一度に最大32ファイルにlddを制限し、4つのlddキューを並行して実行します(そう、自家製のhadupがあります)。 -Pオプションを使用すると、lddのパフォーマンスを並列化できます。これにより、マルチコアマシンと適切なディスクで速度が向上します(この場合、フォッピングが行われますが、lddよりも低速の場合は、並列処理が役立ちます... )



パート4:CALC





ファイルの入力で、出力で、すべてのファイルの合計サイズの数値を与える必要があります。 サイズを決定します...しかし、やめてください。 シンボリックリンクはファイルを指していると誰が言いますか? もちろん、シンボリックリンクは、シンボリックリンクまたはファイルを指すシンボリックリンクを指します。 そして、それらのシンボリックリンク...まあ、彼らはやった。



readlinkを追加します。 そして、彼は感染者であり、一度に1つのパラメーターが必要です。 しかし、多くの労力を節約するオプション-fがあります-シンボリックリンクか単なるファイルかに関係なく、ファイル名を表示します。



| xargs -n 1 -P 16 readlink -f | sort -u



...したがって、duを使用してサイズを決定します。 ここでは単純に-Cオプションを使用できます。これは、数値を合計して答えを出しますが、トレーニングコースでは単純な方法を探していません。 だから、muhlezhなしで。



| xargs -n 32 -P 4 du -b | awk '{sum + = $ 1} END {printf "%i \ n"、sum}'



sort -uが必要なのはなぜですか? 事実、私たちは何度も繰り返します。 リストから一意の値を選択する必要があります。つまり、リストをセットに変換します。 これは単純な方法で行われます。リストをソートし、ソート時に繰り返し行を捨てるということです。



ワンライナー、恐ろしい





すべてを一緒に書きます:



(find / proc / -name exe -ls 2> / dev / null | awk '{print $ 13}' | tee / dev / stderr | xargs -n 32 -P 4 ldd 2> / dev / null | grep / | awk '{print $ 3}')2>&1 | sort -u | xargs -n 1 -P 16 readlink -f | xargs -n 32 -P 4 du -b | awk '{sum + = $ 1} END {print sum}'



(生のタグをこの恐怖に入れなかったので、RSSフィードを引き裂かないように、行が自動的にラップされます。私を愛してください。)



もちろん、これは最初に与えられたものよりも優れています。 一言で言えば、恐怖と恐怖。 あなたが98レベルの第一人者であり、99番目のスイングをしている場合、そのようなシングルラインのプレイヤーは仕事の通常のスタイルになることができます...



ただし、10レベルの攻撃に戻ります。



まともな眺め



切りたいです。 読みやすくデバッグされたコード用。 正気。 コメントアウト。



したがって、最初の形式の書き込みに戻ります:(EXE_LIST | tee / dev / stderr | LIB_LIST)2>&1 | 計算



読みやすい まあ、おそらくはい。



EXE_LISTを適切に記述する方法を理解することは残っています



オプション1:機能の使用:



 EXE_LIST (){ find /proc/ -name "exe" -ls 2>/dev/null|awk '{print $13}' } LIB_LIST (){ xargs -n 32 -P 4 ldd 2>/dev/null|grep /|awk '{print $3}' } CALC (){ sort -u|xargs -n 1 -P 16 readlink -f|xargs -n 32 -P 4 du -b|awk '{sum+=$1}END{print sum}' } (EXE_LIST |tee /dev/stderr|LIB_LIST) 2>&1 | CALC
      
      







そして少し機能的な貴族:



 EXE_LIST () ( find /proc/ -name "exe" -ls 2>/dev/null|awk '{print $13}' ) LIB_LIST () ( xargs -n 32 -P 4 ldd 2>/dev/null|grep / |awk '{print $3}' ) CALC() ( sort -u|xargs -n 1 -P 16 readlink -f|xargs -n 32 -P 4 du -b|awk '{sum+=$1}END{print sum}' ) (EXE_LIST |tee /dev/stderr|LIB_LIST) 2>&1 | CALC
      
      







もちろん、FNPの精製者は、型推論、制御、怠controlな計算はなく、一般的に、これを機能的なスタイル(狂気とポルノ)のリスト処理と見なします。



ただし、これはコードであり、機能し、簡単に記述できます。 実際の製品環境である必要はありませんが、3時間の単調な作業から3分間の興味深いプログラミングを残すツールに簡単になります。 これは、システム管理者の詳細です。



私が見せたかった主なこと:シェルでシーケンスを処理するための機能的なアプローチは、直接反復よりも読みやすく、かさばらないコードを提供します。 ちなみに(xargsの並行性により)動作は高速です。



スケーラビリティ



「シェルhudup」のさらなる開発は、gnu parallelsユーティリティです。これにより、コードを複数のサーバーで並列に実行できます。



All Articles