シェルプログラミング、一時ファイルの回避


8

私はよく同じパターンに従ってKSHシェルスクリプトを作成します。

  • (1)1つ以上のコマンドから出力を取得する
  • (2)grep | cut | awk | sedを使用してフォーマットし、画面またはファイルに出力します

そのために、(1)の出力を一時ファイルに保存し、そのファイルに対して(2)でフォーマットを行うことがよくあります。

そのコードを例にとります:

TMPFILE=file.tmp

# If tmpfile exists rm it.
[ -f $TMPFILE ] && rm -f $TMPFILE

for SERVICE in $(myfunc); do
    getInfo $SERVICE > $TMPFILE # Store raw output in the TMPFILE

    # I retrieve the relevant data from the TMPFILE
    SERV_NAME=$(head -1 $TMPFILE | sed -e 's/ $//')
    SERV_HOSTNAME=$(grep HOSTNAME $TMPFILE | cut -d "=" -f2)
    SERV_ARGS=$(grep Arguments $TMPFILE | cut -d ":" -f2)

    print $SERV_NAME $SEP $SERV_HOSTNAME $SEP $SERV_ARGS
    rm -f $TMPFILE #rm the TMPFILE in vue of next iteration
done

パイプ、リダイレクト、その他を使用して、毎回ファイルがディスクに書き込まれるのを回避する方法はありますか?

それが役立つ場合は、kshバージョンM-11 / 16 / 88iを使用しています


3
シェルスクリプトでALL_CAPS変数名を避け、その名前空間をシェルによって予約されているものとして扱い、PATH他のシェルや環境変数などの重要なものを壊さないようにすることは良い形です。 TMPFILE大丈夫かもしれませんが、TMPDIR特別なので、あなたは本当にその綱渡りをしたいですか?
jw013

後世のために:この1つの重複としてマークされた別の質問unix.stackexchange.com/questions/63923/…には、ここでも使用できる名前付きのfifoパイプに関する回答が含まれています(ただし、おそらく、この特定のケース)。
goldilocks 2013

@goldilocks:2つの質問を1つに統合できるかもしれません。これを行うためにモデレーターに連絡できますか?
rahmu 2013

@rahmu:他の質問にフラグを付けました。それは今の力次第だと思います...
ゴルディロックス2013

回答:


9

あなたのコードは、一時ファイルを使用する完全に正当化された例のように見えます。私はとどまるだろう:このアプローチに固執する。本当に変更する必要があるのは、一時ファイルの作成方法だけです。のようなものを使う

 TMP=$(tempfile)

または

 TMP=$(mktemp)

または少なくとも

 TMP=/tmp/myscript_$$

この方法では、名前を簡単に予測(セキュリティ)できず、同時に実行されているスクリプトの複数のインスタンス間の干渉を排除できます。


2
厳密には、変数の割り当てに引用符は必要ありません。
グレン・ジャックマン、2011

1
@glenn True、この場合、違いはありません。通常、各コマンドはスペースなしの文字列を生成するためです。ただし、コマンド出力を変数に割り当てる場合は引用符を付けるのが良い習慣です。そのため、このままにしておきます。
rozcietrzewiacz

区別のために、最後の例の引用符を削除しました。
rozcietrzewiacz

3
@rozいいえ、ポイントを逃しました。シェルでの変数の割り当ては、展開が行われる前に認識され、変数の割り当てではフィールド分割は行われません。したがって、var=$(echo lots of spaces); echo "$var"問題なくlots of spaces、出力として生成されます。誰も言及していない実際の警告は、コマンド置換はすべての末尾の改行を取り除くことです。これはここでは問題ではなく、たとえば、mktemp改行が末尾に付いたファイル名が作成されている場合にのみ問題になります。必要な場合の通常の回避策はvar=$(echo command with trailing newline; echo x); var=${var%x}です。
jw013 2012

1
@ jw013はい、これに気付きました-答えを1年前に書いたときは気が付きませんでした。指摘してくれてありがとう!(修正中...)
rozcietrzewiacz 2012

5

変数を使用できます:

info="$(getInfo $SERVICE)"
SERV_NAME="$(head -1 $TMPFILE <<<"$info" | sed -e 's/ $//')"
...

からman ksh

<<<word       A  short  form of here document in which word becomes the
              contents of the here-document after any parameter  expan-
              sion,  command  substitution, and arithmetic substitution
              occur.

利点は次のとおりです。

  • 並列実行を有効にします。
  • 私の経験では、これは一時ファイルよりも高速です。データが多すぎてスワッピングが発生しない限り、データ量は桁違いに速くなります(HDキャッシュバッファーを除外するだけで、データ量が少ない場合とほぼ同じです)。
  • 他のプロセスやユーザーがデータを台無しにすることはできません。

<<<が私のkshに存在しないようです。エラーが発生し、manページでそれを見つけることができないようです。私はksh88を使用しています。このバージョンにこの機能が必要ですか?
rahmu

いいえ。私は正しいmanページをチェックしなかったと思います(Webページにバージョン番号の記載がありませんでした:/)
l0b0

<<<あるbashは「ここに文字列」。他のシェルには表示されないと思います。(ああ、zsh多分...)
rozcietrzewiacz '29 / 09/29

2
@rozcietrzewiacz:のGoogle man ksh。それは確かにそこで言及されました。
l0b0

3
bashがhere-stringsとhere-docsをどのように実装するかを推測してください。sleep 3 <<<"here string" & lsof -p $! | grep 0rsleep 30251 anthony 0r REG 253,0 12 263271 /tmp/sh-thd-7256597168 (deleted)—はい、一時ファイルを使用します。
derobert 2012

2

次の2つのオプションがあります。

  1. (を使用した例ではgetInfo)データを1回取得し、それをファイルに保存します。

  2. 毎回データをフェッチし、ローカルに保存しない、つまりgetInfo毎回呼び出す

再処理/再フェッチを回避するために一時ファイルを作成する際に問題は発生しません。

一時ファイルをどこかに残しておくことが心配なtrap場合は、スクリプトを強制終了または中断した場合に備えて、必ず一時ファイルを削除してください。

trap "rm -f $TMPFILE" EXIT HUP INT QUIT TERM

を使用mktempして、一時ファイルに一意のファイル名を作成します。


1

ファイルを生成する代わりに、シェル割り当てステートメントを作成し、その出力を評価します。

for SERVICE in $(myfunc); do
    eval $(getInfo $SERVICE |
               sed -n -e '1/\(.*\) *$/SERV_NAME="\1"/p' \
                   -e '/HOSTNAME/s/^[^=]*=\([^=]*\).*/SERV_HOSTNAME="\1"/p' \
                   -e '/Arguments/^[^:]*:\([^:]*\).*/SERV_ARGS="\1"/p')
    print $SERV_NAME $SEP $SERV_HOSTNAME $SED $SERV_ARGS
done

または、情報を印刷したいだけの場合:

for SERVICE in $(myfunc); do
    getInfo $SERVICE | awk -vsep="$SEP" '
        BEGIN{OFS=sep}
        NR == 1 { sub(/ *$/,""); SERV_NAME=$0 }
        /HOSTNAME/ { split($0, HOST, /=/; SERV_HOSTNAME=HOST[2]; }
        /Arguments/ { split($0, ARGS, /:/; SERV_ARGS }
        END { print SERV_NAME, SERV_HOSTNAME, SERV_ARGS }'
done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.