スクリプトで絶対パスとスペースを含むパス内のすべてのファイルを一覧表示する方法

スクリプトで絶対パスとスペースを含むパス内のすべてのファイルを一覧表示する方法

パスをパラメータとして受け入れ、パス内のすべての.mkvファイルを処理するスクリプトを作成しています。パスは絶対パス、相対パス、または単一ファイルにすることができます。ただし、繰り返しファイルの絶対パスがまだ必要です。

これまで私がしたことは次のとおりです。

#!/bin/bash

path=$1
if ! [ -e "$path" ]; then
  path=$(pwd)
fi

find "$path" -type f -name '*.mkv' | sort | xargs realpath | while read file; do
  echo "Processing $file"
done

これは、パスおよび/またはファイルにスペースがない場合に機能しますが、スペースによって失敗します。

正しいアプローチは何ですか?

答え1

そしてzsh

#! /bin/zsh -
for file (${${1-.}:P}/**/*.mkv(ND.))
  print -r processing $file
  • ${1-.}Bourneシェルのように、.if $1is not setに拡張されます(if $#== 0; ifが悪いインタフェースであり、$1isが存在しないファイルではありません。これはユーザーにとって大きな驚きです)。
  • ${param:P}realpath()その拡張に拡張されます。**/シンボリックリンクに従わずに.修飾子は選択のみを行うため、すべてのファイルに対して同じことを行う必要はありません。定期的なファイルなのでシンボリックリンクではありません。
  • **/すべてのレベルのサブディレクトリと一致
  • Nnullglob、Ddotglob.のGlob修飾子定期的なただ。

答え2

すべてのファイルの代わりにパス自体を前処理できます。

find "$(realpath -- "$path")" -type f -name '*.mkv' -exec printf 'Processing %s\n' {} +

printf実際にファイルでやりたいことに置き換えてください。+呼び出しごとに複数のファイルを処理できることを示します。そうでない場合は、代わりに使用してください\;

答え3

Stephenのコメントに同意します。常に-exec出力後処理をお勧めしますfind。必要に応じて、-print0明示的なファイル名の区別を常に許可するツールを使用してください。入力のファイル名を使用するほとんどのツールは、一種の、またはフラグ--nullをサポートして、入力をゼロバイト区切りとして解釈するようにします。--zero-z-0

あなたの場合は、おそらくzshを好むでしょう。しかし、bashに固執したいと仮定すると、何の問題もなく同じことができます。sortとにかくワイルドカードはアルファベット順に並べられているので省略できます。 bashは再帰的なグロービングを実行できるため、省略することができますfind(zshはグロビング時にファイルの種類を制限することができ、bashでは実際にファイル性をテストする必要があります)。とにかくファイルを処理するので-f省略できます。xargs:

#!/bin/bash
# Fail when something goes wrong!
set -e
# enable recursive globs, and don't fail when there's nothing there
shopt -s globstar
shopt -s dotglob
shopt -s nullglob

inpath="$1"
if ! [ -e "${inpath}" ]; then
  inpath="$(pwd)"
fi

for file in "${inpath}/**/*.mkv"; do
  # check whether regular file, skip if not
  [ -f "${file}" ] || continue
  fullpath="$(realpath -- "${file}")"
  echo  "Processing ${fullpath}" # Could / might prefer to use printf here instead
done

あなたできる基本的な並べ替えが実際に必要なものではない場合に備えて、項目を別々に並べ替えてください。交換する必要があります

for file in "${inpath}/**/*.mkv"; do

似たようなもの

unsorted=( ${inpath}/**/*.mkv )
readarray -d '' -t sorted_files < \
    <(printf '%s\0' "${unsorted[@]}" \
        | sort -z --whateveroptions \
     )
for file in "${sorted_files[@]}" ; do

readarray渡された文字列に区切り文字を設定するオプションがあります-d。空の文字列を渡すと、ゼロバイトが区切り文字として使用されます。readarrayトリム-t(ここでは必要ありませんが安全なデフォルト値であることが確認されています)は、sort -zゼロバイトを区切り文字として使用します。"${arrayname[@]}"この配列の繰り返し可能な要素のリストに展開されますが、要素にスペースと改行が含まれていても安全に使用できます。

zshでは、ワイルドカードで見つかったファイルを並べ替えることもできます。可能な場合は必ず zsh を使用してください。

関連情報