bashを使用してディレクトリ内のすべてのファイルを調べて、どのファイル(ある場合)にコンテンツが記録されているかを確認するにはどうすればよいですか?

bashを使用してディレクトリ内のすべてのファイルを調べて、どのファイル(ある場合)にコンテンツが記録されているかを確認するにはどうすればよいですか?

複数の「人」に対して動作するスクリプトを実行し、各人の出力ファイルとエラーファイルを生成します。これをこう表現してみよう:

output_alice.txt
error_alice.txt
output_bob.txt
error_bob.txt
.
.
.

error_<name>.txtスクリプトがエラーで終了した「人」をすばやく識別できるように、すべてのファイルからエラー()を検索し、コンテンツが記録されたファイル(空のファイルではない)をエコーするコマンドが必要です。見つけることができるショートカットがありますか?私はgrepを使って文字列(例えば)でこれを行う方法を知っていますが、grep -r <substring> .そこに何があるかどうかを確認する方法はわかりません。

答え1

bashはそうではありません。端末、それは多くのものの一つですシェルこれは、コマンドの実行に特化した特定のプログラミング言語の通訳です。ほとんどのアプリケーションと同様に、入力/出力を端末デバイスまたは他の種類のファイルに接続できます。

bashおよび他のほとんどのUnixシェル言語で、現在の作業ディレクトリにl1つ以上の名前付き行を含むファイルを一覧表示するには、次のようにします。error_anything.txt

grep -l '^' error_*.txt

^トピックの先頭に一致する正規表現はどこにありますか?トピックファイルの各行はですgrep

空でないテキスト行が1つ以上ある人の場合:

grep -l . error_*.txt

ここでは.単一文字と一致します。ロケールとは異なる文字マップエンコーディングを使用するファイルは、その内容をテキストとしてデコードできない場合、空でない行と一致しない可能性があります。

また、すべてのgrep実装が終了していない行が1つだけ含まれるファイルを報告するわけではありません(の出力に示すように、1つは行区切り文字がありませんprintf invalid-text-as-missing-the-last-newline)。

別の方法は、少なくとも1バイトを含むファイルを見つけることです。

find -L . ! -name . -prune -name 'error_*.txt' -type f -size +0c

これは、そのタイプではないファイルを無視するという利点もあります。定期的な(例:ディレクトリ、ソケット...)

またはzshシェルを使用してください。

print -rC1 -- error_*.txt(N-.L+0)

シンボリックリンクの場合、動作はターゲットのサイズと種類を考慮してullglobの動作と同じです(ullglobの場合、-一致するファイルがないとエラーは報告されません)。-L.-type fL+0-size +0cNN

これはプレフィックスを含まないという利点があり、./ユーザー名をロケールのテキストにデコードできない場合にも機能し、(語彙的に)ソートされたリストを提供します。

rこれを拡張して、ユーザー名(最初の名前の後のファイルoot名部分_)のみを印刷できます。

{}{ print -rC1 -- ${@#*_}; } error_*.txt(N-.L+0:r)

errorコマンドの実行後に変更されたファイルを一覧表示するには、コマンドを-newer実行する前に編集したファイルの述語を使用してfind比較できます。touch

touch .before
my-command-that-may-write-to-error-files
find -L . ! -name . -prune -name 'error_*.txt' -type f -size +0c -newer .before

findzshでは、コマンドを次のように置き換えることができます。

print -rC1 -- error_*.txt(N-.L+0e['[[ $REPLY -nt .before ]]'])

一部の実装では、置き換えることfindができますが、深さ0()のファイルは他の基準と一致しないため、ここでも機能します(一致も一致も一致しません)。! -name . -prune-mindepth 1 -maxdepth 1-maxdepth 1.-name 'error_*.txt'-type f

dateandのGNU実装(述語を導入した実装findでもある)を使用すると、次のようにしてこのファイルの生成を回避できます。find-maxdepth.before

before=$(date +'@%s.%N')
my-command-that-may-write-to-error-files
find -L . -maxdepth 1 -name 'error_*.txt' -type f -size +0c -newermt "$before"

を使用するとき、または(後に)にzsh置き換えることができます。再呼び出しを避けることができます。before=$(date +'@%s.%N')print -Pv before '@%D{%s.%N}'before=${(%):-@%{%s.%N}D}before=@$EPOCHREALTIMEzmodload zsh/datetimefindグローバル予選また、一時変数に匿名関数を再利用することもありますが、これは非常に複雑になります。

zmodload zsh/stat
zmodload zsh/datetime
() {
  my-command-that-may-write-to-error-files
  print -rC1 error_*.txt(N-.L+0e['
    stat -F %s.%N -A2 +mtime -- $REPLY && (( $2 > $1 )) '])
} $EPOCHREALTIME

少なくともLinuxでは、システムとファイルシステムはナノ秒精度をサポートしますが、粒度ははるかに小さいです。初期呼び出しdateまたは参照より前の値を変更するときは変更時間が設定されているため、$EPOCHREALTIME実行に100分の1秒もかからないコマンドではこの方法が機能しない可能性があります。 1秒を削除しNてそれを>またはに置き換える方が良い方法かもしれません(実装がサポートしている場合は可能ではありません)。>=-newer! -olderfind

答え2

GNU は空のfindファイルをリストする POSIX 以外のオプションを提供します。テストを無効にするだけです。

find /path/to/dir -type f -name 'error_*.txt' ! -empty

~のためいいえ-maxdepth 1検索するパスの後にサブディレクトリを追加します。

POSIXではfindファイルサイズの確認が0可能です。

find /path/to/dir -type f -name 'error_*.txt' ! -size 0

答え3

grep for は.すべての文字を意味します。空のファイルには文字がないため、検索時に空でない.ファイルが表示されます。たとえば、

$ touch empty1 empty2 empty3
$ echo "not empty!" > non_empty
$ ls -l 
total 4
-rw-r--r-- 1 terdon terdon  0 Aug 11 13:13 empty1
-rw-r--r-- 1 terdon terdon  0 Aug 11 13:13 empty2
-rw-r--r-- 1 terdon terdon  0 Aug 11 13:13 empty3
-rw-r--r-- 1 terdon terdon 11 Aug 11 13:13 non_empty

今、私たちは以下をgrepします。

$ grep -- . ./*
non_empty:not empty!

そして名前を調べてください。

$ grep -l -- . ./*
non_empty

grep .空白行(1文字以上)を持たないファイルは見つかりません\n。これを行うには、grep '^'提案どおりに使用する必要があります。スティーブンの答え

答え4

GNU sed専用です。次のコマンドの代替と同様にgrep:

sed -sn 1F error_*.txt

Fマニュアルページでコマンドが見つかりませんでしたが、うまくいきます。特に、空でないファイルの最初の行にファイル名を挿入します。sed -i 1F *

関連情報