while ループで一致しないファイル名を表示する

while ループで一致しないファイル名を表示する

files.txtいくつかのファイル名を含むというファイルがあります。ファイルを読み取り、aを実行して見つかったfindファイル名を返すコマンドがあります。その逆に行う方法が気になります。私が望むのは、見つからなかったファイル名を表示することです。

例:

$ ls
file1  file2  file3  files.txt
$ cat files.txt
file1
file2
file3
file4
$ while read x; do (find . -iname "$x"); done < files.txt
./file1
./file2
./file3

file4この例では、ファイル名は.txtにありますがフォルダにはないため、結果に表示したいと思います。

答え1

使用grep(現在のフォルダ内のファイルの名前に改行文字が含まれていないと仮定):

$ grep -v -i -F -x -f <(printf '%s\n' *) files.txt
file4

オプション:

  • -v一致を取り消し、一致しない行を選択します。
  • -f printf '%s\n' *現在のディレクトリのファイルを一覧表示しますgrep
  • -Fパターンを固定文字列として解釈します(正規表現の代わりに)。
  • -x全行と一致
  • -i大文字と小文字を区別しない一致(あなたと同じ-iname)。

答え2

使用find:

while IFS= read -r name; do
    if [ -z "$(find . -iname "$name")" ]; then
        printf 'Not found: %s\n' "$name"
    fi
done <files.txt

このループはファイルから一度に1つのファイル名を読み取り、files.txt現在のディレクトリの下の階層全体でその名前を大文字と小文字を区別せずに検索します。名前が見つからない場合(find何も出力されません)、メッセージが表示されます。

findコマンドが通常のファイル(ディレクトリなどではない)のみを検索できるようにするには、-type fコマンドに追加します。

答え3

find深さがディレクトリの場合は、次を使用する代わりにこれを実行できます。

while IFS= read -r file; do 
  [[ -f "${file}" ]] && echo "${file} exists" \
  || echo "$file doesn't exist"; 
done < files.txt

答え4

そしてzsh

set -o extendedglob # best in ~/.zshrc
files=(*(ND))
list=(${(f)"$(<files.txt)"})

not_found=(${list:#(#i)(${(~j[|])files})})
if (($#not_found)); then
  echo "Not found:"
  printf ' - %q\n' $not_found
fi

not_in_list=(${files:#(#i)(${(~j[|])list})})
if (($#not_in_list)); then
  echo "Unexpected files (not in files.txt):"
  printf ' - %q\n' $not_in_list
fi

現在のディレクトリ(配列)にあるファイルリストと(配列)にあるファイルリストの$files間で大文字と小文字を区別せずに減算を実行します。files.txt$list

zsh実際に配列減算演算子()がありますが、${array1:|array2}大文字と小文字を区別しない場合はここでは使用できません。

代わりに${array:#pattern}要素に拡張される演算子を使用します。$array とは別に一致しますpattern

ここのパターンは、|()を使用し、大文字と小文字を区別しない一致のために演算子を使用してj[|]他の配列の要素を連結することによって構築されます。extendedglob (#i)

関連情報