「検索」出力でシンボリックリンクではなくファイル数を計算する

「検索」出力でシンボリックリンクではなくファイル数を計算する

Locateコマンドで渡された非シンボリックファイルの数を数えようとしています。私はいくつかのオプションを試しましたが、これが最も有望だと思います。

locate -r "$PWD.*\.c$" | xargs -0 -I{} test -f {} && echo "regular file" | wc -l

問題はそれがうまくいかないことです。

合計30個のファイルがあり、そのうちの1つはシンボリックリンクwc -lなので29

私はxargsそれを完全にスキップしようとしました。

locate -r "$PWD.*\.c$" | test -f && echo "regular file" | wc -l

頑張ったいいえシンボリックリンク:

locate -r "$PWD.*\.c$" | test ! -h && echo "regular file" | wc -l
locate -r "$PWD.*\.c$" | test ! -L && echo "regular file" | wc -l

locate出力をパイプし、通常のファイル数とシンボリックリンク数を計算する最も効率的な方法は何ですか?


コメントに返信する

一部の人々は演出が好きですlocatefind不可知論者であればいいのですが、できればlocate使いたいです。コメントが投稿されており、質問に回答したいと思います。

  • updatedb最初の実行には30秒かかりますが、後続の実行には4秒しかかかりません。 5分ごとに走るのは、1cron日1回というデフォルト値に対する無謀な反応です。しかし、ノートパソコンのCPU使用量は10~20%に過ぎず、レックもまったくありません。
  • キャッシュを削除した後、ファイルをfind探すのに1分9秒かかります。
  • キャッシュを削除した後、locate同じファイルを見つけるのに1秒かかります。

以下は、システムに複製できるいくつかのベンチマークです。

$ sudo -i
# sync; echo 1 > /proc/sys/vm/drop_caches; sync; echo 2 > /proc/sys/vm/drop_caches; sync; echo 3 > /proc/sys/vm/drop_caches; exit
logout

$ time locate .hidden.c | wc -l
1

real    0m0.790s
user    0m0.758s
sys     0m0.028s

$ sudo -i
# sync; echo 1 > /proc/sys/vm/drop_caches; sync; echo 2 > /proc/sys/vm/drop_caches; sync; echo 3 > /proc/sys/vm/drop_caches; exit
logout

$ time find / iname '.hidden.c'  2>/dev/null | wc -l
1888926

real    1m9.044s
user    0m5.158s
sys     0m15.004s

$ sudo -i
# sync; echo 1 > /proc/sys/vm/drop_caches; sync; echo 2 > /proc/sys/vm/drop_caches; sync; echo 3 > /proc/sys/vm/drop_caches; exit
logout

$ time sudo updatedb

real    0m29.323s
user    0m1.267s
sys     0m4.784s

$ time sudo updatedb

real    0m3.592s
user    0m0.479s
sys     0m1.211s

find間違いなくそれよりも強力ですlocateが、locate数倍速く、構文も覚えやすくなります。

実際に、今日生成されたファイルを含めるか、今日削除されたファイルを除外するようにデータベースを更新するには、コマンドの引数を一度sudo updatedbに実行または渡す必要があることに注意してください。しかし、一方でパラメータを渡すことを覚えておく必要があります。-ulocatefind2>/dev/null

まず、私のラップトップは正しく動作せず、2番目に怠惰なので、5分ごとにcron実行することにしました。updatedb

答え1

このコマンドは閉じます。

locate -r "$PWD.*\.c$" | xargs -0 -I{} test -f {} && echo "regular file" | wc -l

質問:

  • xargsと一緒にNULLで区切られた入力を使用しますが、locateNULLで区切られた出力は提供しません。
  • 単一のパイプラインではなく、パイプライン全体に対して実行され&& echoます。locate | xargstest

努力する:

locate -0r "$PWD.*\.c$" | xargs -0 -I{} sh -c 'test -f "$1" && echo "regular file"' _  {} | wc -l
  • locate以下を使用して、Nullで区切られた出力を有効にします。-0
  • testandと結合(各呼び出しが複数のファイルを処理するようにechoパラメータループで改善することができます)sh -csh

にはまだ正規表現演算子があります$PWD

答え2

そしてzsh

set -o extendedglob # best in ~/.zshrc
c_regular_files=(
  ${(0)^"$(locate -0 "${${PWD%/}//(#m)[]\\*?]/\\$MATCH}/*.c")"}(N.)
)
echo there are at least $#c_regular_files regular files whose name ends in .c
  • forで[、および?をエスケープする必要があり、ワイルドカード演算子として解釈しないでください(ファイル名にcommon、を含むより多くの演算子を持つ正規表現の方が悪い)。\*$PWDlocate-r.
  • $PWD==は/特別に処理する必要があります。$PWD代わりに使用すると、これを${PWD%/}実行しlocate -0 "//*.c"、何も返しません。
  • -0ファイルはNULで区切られています(ファイルパスに改行が許可されているため、改行は効果がありません)。
  • .ある定期的な文書。対照的に[ -f、一般ファイルへのシンボリックリンクは除外されます。 Symlink以外のすべての.cファイル(ディレクトリ、fifo、ソケットなどの他の種類のファイルを許可する)が必要な.場合^@

それにもかかわらず、locate返されたリストはlocateデータベースが最後に更新された時刻に基づいているため、現実を反映していない可能性があります。

答え3

構文解析の代わりにlocate(壊れやすく、データベースが最後に更新されてから変更されたアイテムまたは利用できないアイテムが見逃される可能性があります)みんなユーザー)find、。

.c次のコマンドは、現在のディレクトリ内のすべての一般ファイル(シンボリックリンクではない)のファイルを探します。

find . -type f -name '*.c'

与えられたディレクトリ構造

.
|-- file-a.c
|-- file-b.c
|-- file-c.c
|-- file-d.c
|-- link-b.c -> file-b.c
`-- link-d.c -> file-d.c

が返されます

./file-a.c
./file-b.c
./file-c.c
./file-d.c

カウントしてください:

find . -type f -name '*.c' | wc -l

またはファイル名に改行文字が含まれている場合

find .//. -name '*.c' -type f | grep -c //

シンボリックリンクで同じことを行うには-type fに変更-type l

答え4

GNU Parallelを使用すると、次のようになります。

locate -r "$PWD.*\.c$" | parallel 'test -f {} && echo "regular file"' | wc -l

ご覧のとおり、元の試みに非常に近いです。

ヒット数が100個未満の場合は、GNU Parallelを使用して失敗したジョブの数を最大100個に設定できます(拡張されていません)。

ls  *txt | parallel \! test -f {}
echo $?

より早く必要な場合:

locate -r "$PWD.*\.c$" |
  perl -ne 'chomp; -l $_ or $s+= -f $_; END{print "$s\n"}'

または組み合わせ:

locate -r "$PWD.*\.c$" |
  parallel --block 10k --pipe -q perl -ne 'chomp; -l $_ or $s+= -f $_; END{print "$s\n"}' |
  awk '{s+=$1} END {print s}'

関連情報