スクリプトで使用できるように、ファイルがどのデバイスにあるかを調べたいと思います。私はここまで到達できます:
$ df .
Filesystem 512-blocks Used Available Capacity Mounted on
/dev/disk0s2 498438976 294369520 203557456 60% /
しかし、この出力は不器用に感じます。これを解析して2行目の最初の「単語」を取得するよりも良い方法はありますか?
私が本当に必要としているのは、次のコマンドにパイプできるように、次のようなものです。
$ somecommand .
/dev/disk0s2
できれば「df」出力を文字列ハッキングすることなく、これをどのように達成できますか?
シェルだけでそれを行うことができます(bash
、dash
、ksh
、zsh
で動作します):
df . | (read a; read a b; echo "$a")
または、出力が不要で(結果は$ aに保持されます)、シェルがプロセス置換をサポートしている場合(bash
、zsh
など):
{ 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
ソリューションとは比較されません。)
単純なプログラムの力をほんの少しに連結するのは通常の NIXの方法 です。したがって、df
の出力を何らかのフィルターにパイプする必要はありません。
df /path/to/file | sed -n '2{s/ .*$//;p}'
-n
印刷行を自動的に抑制します。2{}
2行目で囲まれたコマンドを実行します。s/ .*$//
は最初のスペースからすべてを破棄し、p
は残っているものを出力します。長い入力を解析し、2行目(またはn行目)だけが必要な場合にq
の後にp
を追加すると、少し高速化することもできます。
sed
、awk
を次のように使用して単純な1行を使用できます
df . | sed '2!d' | awk '{print $1}'
sed
で、2d
を指定すると、2が削除されますnd ライン。 !
を追加するとこれが無効になるため、他のすべての行が削除され、2行目が出力されます。次に、awk
コマンドは最初の列の値を表示します。
出力:
/dev/disk0s2
df
の出力を解析することは、移植可能に実行できる最善の方法です。 -P
をdf
に渡して、出力が奇妙な方法でフォーマットされないようにします(最初のフィールドを取得しているので、どこでも安全ですが、マウントポイントを取得するには-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は単に!
に置き換えられます)
ただし、手動で行うこともできます。