web-dev-qa-db-ja.com

スペース付きのbash変数(ディレクトリ)

リストされたディレクトリ内のスペースを維持しながら、/foo内のすべてのファイルをエコーし​​ようとしています。現在、"bar1 bar2"というタイトルのディレクトリは、すべて新しい行に/path/to/foo/bar1/、次に/bar2、次にfile.txtとしてエコーアウトされます。 /path/to/foo/bar1 bar2/file.txtまたは/path/to/foo/bar1\ bar2/file.txtをエコーするために必要です。

for file in $(find /path/to/foo/ -type f); do
  if [[ ... ]]; then
    echo "${file}"
  fi
done

また試した:

for file in $(find /path/to/foo/ -type f | sed 's/ /\ /g'); do
  if [[ ... ]]; then
    echo "${file}"
    echo "$file" # variations i've tried
    echo $file   # variations i've tried
    echo ${file}
    otherCommand "${file}"
  fi
done
6

問題は_$file_ではなく、$(find…)の使用法にあります。

通常の変数と$(…)プロセス置換はどちらも、まったく同じ種類の単語分割の対象になります。 $(find…)を二重引用符で囲むと、すべての出力を含むsingle Wordが得られます。そうしないと、空白で分割されます– not改行またはおそらく予想したその他の魔法の境界だけです。

上記の重要な部分は、変数を展開するときにバックスラッシュエスケープがnot処理されることです。スペースで分割されているだけで、それだけです。

(言い換えると、_$file_を引用しても、そもそも正しい値がなかったため、役に立ちませんでした!)

すべてのファイルを再帰的に処理する場合、オプションは次のとおりです。

  1. 別の方法でfindからの出力を読み取ります。

    _find … -print | while read -r file; do
        echo "Got $file"
    done
    _

    (補足:ファイル名には技術的に改行も含まれる場合があるため、まれに、それを防ぐことができます。

    _find … -print0 | while read -r -d "" file; do
        echo "Got $file"
    done
    _
  2. 少量のファイルの場合は、bashの拡張ワイルドカードを使用します。

    _shopt -s globstar
    for file in /path/to/foo/**; do
        echo "Got $file"
    done
    _

ディレクトリが大きい場合、_find | while read_の利点は、ストリーミングされることです。スクリプトは、結果が来たときに処理します。一方、$(find)とワイルドカードの両方が最初にすべてをメモリに収集し、次に(おそらく大規模な)結果を返す必要があります。

一方、パイプを使用すると、全体whileループ(readコマンドだけでなく)に影響するため、実行する場合は何でもキーボード入力を読み取りたい場合は、元のstdinを手動で提供する必要があります。 _vim "$file" < /dev/tty_。

16
user1686