web-dev-qa-db-ja.com

ファイルが存在するデバイスを見つける(そしてそれをスクリプトで使用する)方法は?

スクリプトで使用できるように、ファイルがどのデバイスにあるかを調べたいと思います。私はここまで到達できます:

$ df  .
Filesystem   512-blocks      Used Available Capacity  Mounted on
/dev/disk0s2  498438976 294369520 203557456    60%    /

しかし、この出力は不器用に感じます。これを解析して2行目の最初の「単語」を取得するよりも良い方法はありますか?

私が本当に必要としているのは、次のコマンドにパイプできるように、次のようなものです。

$ somecommand .
/dev/disk0s2

できれば「df」出力を文字列ハッキングすることなく、これをどのように達成できますか?

3
antonyh

シェルだけでそれを行うことができます(bashdashkshzshで動作します):

df . | (read a; read a b; echo "$a")

または、出力が不要で(結果は$ aに保持されます)、シェルがプロセス置換をサポートしている場合(bashzshなど):

{ read; read a b;}< <(df .)

そして、他のソリューションの速度との比較を次に示します。

# pure Shell solution 1

bash-4.2$ time for i in $(seq 500); do df . | (read a; read a b; echo "$a"); done > /dev/null
1.899

(dash) $ time -f '%e' dash -c 'for i in $(seq 500); do df . | (read a; read a b; echo "$a"); done > /dev/null'
1.05

(ksh) $ time for i in $(seq 500); do df . | (read a; read a b; echo "$a"); done > /dev/null
    0m1.16s real     0m0.02s user     0m0.12s system

(zsh) manatwork% time (for i in $(seq 500); do df . | (read a; read a b; echo "$a"); done > /dev/null)
1.51s

# pure Shell solution 2

bash-4.2$ time for i in $(seq 500); do { read; read a b;}< <(df .); done
1.192

(zsh) manatwork% time (for i in $(seq 500); do { read; read a b;}< <(df .); done)
3.51s

# other solutions

bash-4.2$ time for i in $(seq 500); do df . | tail -1 | cut -f 1 -d " "; done > /dev/null
1.405

bash-4.2$ time for i in $(seq 500); do df . | sed '2!d' | awk '{print $1}'; done > /dev/null
5.407

bash-4.2$ time for i in $(seq 500); do df . | sed -n '2{s/ .*$//;p}'; done > /dev/null
1.767

bash-4.2$ time for i in $(seq 500); do df . | sed '2!d' | awk '{print $1}'; done > /dev/null
3.334

bash-4.2$ time for i in $(seq 500); do df . | gawk 'NR==2{print $1}'; done > /dev/null
3.013

bash-4.2$ time for i in $(seq 500); do df . | mawk 'NR==2{print $1}'; done > /dev/null
1.747

bash-4.2$ time for i in $(seq 500); do df . | Perl -nae 'print$F[0]if$.==2'; done > /dev/null
2.752

(ここでは機能しないため、statソリューションとは比較されません。)

4
manatwork

単純なプログラムの力をほんの少しに連結するのは通常の NIXの方法 です。したがって、dfの出力を何らかのフィルターにパイプする必要はありません。

df /path/to/file | sed -n '2{s/ .*$//;p}'

-n印刷行を自動的に抑制します。2{}2行目で囲まれたコマンドを実行します。s/ .*$//は最初のスペースからすべてを破棄し、pは残っているものを出力します。長い入力を解析し、2行目(またはn行目)だけが必要な場合にqの後にpを追加すると、少し高速化することもできます。

3
peterph

sedawkを次のように使用して単純な1行を使用できます

df . | sed '2!d' | awk '{print $1}'

sedで、2dを指定すると、2が削除されますnd ライン。 !を追加するとこれが無効になるため、他のすべての行が削除され、2行目が出力されます。次に、awkコマンドは最初の列の値を表示します。

出力:

/dev/disk0s2
1
mtk

dfの出力を解析することは、移植可能に実行できる最善の方法です。 -Pdfに渡して、出力が奇妙な方法でフォーマットされないようにします(最初のフィールドを取得しているので、どこでも安全ですが、マウントポイントを取得するには-Pが必要です。前の列が広すぎる場合は、次の行に追いやられる可能性があります)。

device_name=$(df -P . | awk 'NR==2 {print $1}')

一部のシステムでは、デバイス名に空白(OSXで発生する傾向があるIIRC)を含めることができることに注意してください。このケースを処理するためのポータブルで便利な方法はありません。

Linuxでこれを行うためのより良い方法はないと思います。 statはデバイス番号(stat -c %t .)を提供できますが、/devの下にデバイスエントリが必要な場合は、/procから抽出する必要があります。これはdfの方が優れています。

ecryptfsを使用している場合、上記のソリューションはいずれも機能しないことに注意してください。

ただし、解決策は簡単です。出力を引数としてコマンドを再度呼び出すだけです。つまり、次のようになります。

  • findmnt -no source -T "$(findmnt -no source -T /path/to/file)"
  • df "$(df . | awk 'NR==2{print $1}')" | awk 'NR==2{print $1}'

残念ながらdf . | sed '2!d' | awk '{print $1}'はまったく機能しません。

$ df "$(df . | sed '2!d' | awk '{print $1}')" | sed '2!d' | awk '{print $1}'
bash: !d': event not found

まず、bashは二重引用符内の!を履歴展開として解釈しようとし(コマンド置換内が一重引用符内であることは関係ありません)、それをエスケープします...

$  df "$(df . | sed '2\!d' | awk '{print $1}')" | sed '2!d' | awk '{print $1}'
sed: -e expression #1, char 2: unknown command: `\'

... sedに文句を言わせます(\はコマンド置換内の単一引用符で囲まれているため、文字通りであり、notは単に!に置き換えられます)

ただし、手動で行うこともできます。

0
Stavros