web-dev-qa-db-ja.com

「find -exec」はスペースを含むファイル名をどのように渡しますか?

名前にスペースが含まれているいくつかのファイルを含むディレクトリがある場合、.

$ ls -1 dir1
file 1
file 2
file 3

これらすべてを次のように別のディレクトリに正常にコピーできます。

$ find dir1 -mindepth 1 -exec cp -t dir2 {} +

ただし、find dir1 -mindepth 1の出力にはエスケープされていないスペースが含まれています。

$ find dir1 mindepth 1
dir1/file 1
dir1/file 3
dir1/file 3

printの代わりにprint0を使用しても、出力にはエスケープされていないスペースが含まれています。

$ find dir1 mindepth 1 -print0
dir1/file 1dir1/file 2dir1/file 3

これらのファイルをcpを使用して手動でコピーするには、スペースをエスケープする必要があります。しかし、cpの引数がfindからのものである場合、コマンドの最後に+を使用するか\;を使用するかに関係なく、これは不要であるようです。

これの理由は何ですか?

15
EmmaV

findコマンドは、コマンドを直接実行します。 filename引数を含むコマンドは、シェルやファイル名を変更する可能性のあるその他の要素によって処理されません。とても安全です。

findコマンドラインで{}で表されるファイル名をエスケープする必要がないことは正しいです。

findは、生のファイル名をディスクから直接-execコマンドの内部引数リストに渡します。この場合、cpコマンドです。

8
RobertL

問題は2部構成です。

  • どのようにdoesfind-execを使用して、ファイル名に埋め込まれたスペースの問題に遭遇することなくプログラムを呼び出すことができます。
  • -print0オプションのメリットは何ですか?

最初に、findはシステムコールを作成しています。実際には、 "exec" と呼ばれる関連するコールのグループの1つです。これは、ファイル名を引数としてこの呼び出しに直接渡します。この呼び出しは、ファイル名に関する情報を失うことなく(新しいプロセスの作成後に)直接渡されます。

POSIX find 機能+根拠 で次のように説明されます:

SVR4のfindユーティリティの機能は、-execプライマリの+ターミネータでした。これにより、特殊文字(特にnewline文字)を含むファイル名をグループ化して、そのようなファイル名をxargsにパイプする場合に発生する問題を回避できます。他の実装では、この問題を回避する他の方法が追加されています。特に、ヌルバイトターミネーターを使用してファイル名を書き込んだ-print0プライマリーです。これはここで考慮されましたが、採用されませんでした。 nullターミネーターを使用すると、findの-print0出力を処理するすべてのユーティリティが、現在読み取るnullターミネーターを解析するための新しいオプションを追加する必要がありました。

著しくa -print0 primary」はGNUを指します別の方法で問題を解決するfindおよびxargs。 FreeBSD find および xargs でもサポートされています。 xargs呼び出しに-0オプション( マニュアルページ を参照)を追加した場合、そのプログラムは「nullバイト」文字で終了する行を受け入れます。次に、xargsexec-関数を呼び出してitsを機能させます。 -print0機能と-0機能と+機能の主な違いは、前者はファイル名をパイプで渡すのに対し、後者は渡さないことです。開発者はほとんどすべての機能の用途を見つけます。パイプも例外ではありません。

cp-tオプションを使用するOPの例に戻ります。これは POSIX cp にはありません。むしろ、それは GNU cp によって提供される拡張機能(別名 "非標準機能")です。 xargs-0拡張機能はこの例を改善しませんが、それを効果的に使用できる他のケースがあります。移植可能な代替+があることを覚えておいてください。これはGNU findは受け入れます。

5
Thomas Dickey

これはコメントであるべきですが、大きすぎます。

物事を試してみたい人のために:

渡された位置パラメータをリストするスクリプトを作成し、list_positional_parameters.shと呼びます。

#!/bin/bash

# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_07.html
# Try globbing patterns, e.g. "X[[:digit:]][[:digit:]]" to see what happens

if [ $# -lt 1 ]; then
   echo "Usage: $0 and then at least one parameter"
   exit 1
fi

counter=1

while (($#)); do
   echo "$counter = '$1'"
   # pop positional argument 1 off the stack of positional arguments
   shift
   (( counter++ ))
done

いくつかのディレクトリ$ di​​rでfindを実行します。

find "$dir" -exec ./list_positional_parameters.sh '{}' ';' | less

予想通り、名前にスペースが含まれているかどうかに関係なく、すべての呼び出しで単一のパラメーター、ファイル名のみが存在します。

0
David Tonhofer