名前にスペースが含まれているいくつかのファイルを含むディレクトリがある場合、.
$ 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
からのものである場合、コマンドの最後に+
を使用するか\;
を使用するかに関係なく、これは不要であるようです。
これの理由は何ですか?
find
コマンドは、コマンドを直接実行します。 filename引数を含むコマンドは、シェルやファイル名を変更する可能性のあるその他の要素によって処理されません。とても安全です。
find
コマンドラインで{}
で表されるファイル名をエスケープする必要がないことは正しいです。
find
は、生のファイル名をディスクから直接-exec
コマンドの内部引数リストに渡します。この場合、cp
コマンドです。
問題は2部構成です。
find
-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バイト」文字で終了する行を受け入れます。次に、xargs
はexec-関数を呼び出してitsを機能させます。 -print0
機能と-0
機能と+
機能の主な違いは、前者はファイル名をパイプで渡すのに対し、後者は渡さないことです。開発者はほとんどすべての機能の用途を見つけます。パイプも例外ではありません。
cp
に-t
オプションを使用するOPの例に戻ります。これは POSIX cp にはありません。むしろ、それは GNU cp によって提供される拡張機能(別名 "非標準機能")です。 xargs
の-0
拡張機能はこの例を改善しませんが、それを効果的に使用できる他のケースがあります。移植可能な代替+
があることを覚えておいてください。これはGNU find
は受け入れます。
(これはコメントであるべきですが、大きすぎます。)
物事を試してみたい人のために:
渡された位置パラメータをリストするスクリプトを作成し、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
いくつかのディレクトリ$ dirでfind
を実行します。
find "$dir" -exec ./list_positional_parameters.sh '{}' ';' | less
予想通り、名前にスペースが含まれているかどうかに関係なく、すべての呼び出しで単一のパラメーター、ファイル名のみが存在します。