ターゲットファイル名を調整する必要がない場合は、次のことができます。
$ find -type f -name '*.pat' -print0 | xargs -O cp -t /path/to/dest
ファイル名に改行文字を含めることもできるので安全です。
代替:
$ find -type f -name '*.pat' -print0 | cpio -p -0 -d /path/to/dest
今私が持っている問題は、ターゲットがVFATファイルシステムであるため、ファイル名に特定の文字(「?」など)が許可されないことです。これは、ターゲットファイル名を調整する必要があることを意味します。
それはまるで
for i `find -type f -name '*.pat'` ; do
cp "$i" `echo $i | sed 's/?/_/'`
done
空白のないファイル名でのみ機能します。 IFSを改行に変更できます。しかし、 '\0'をIFSに設定するにはどうすればよいですか?
そして - for ループはファイル (mv/sed) と同じくらい多くのフォーク/exec を発生させます。これは、最初の2つの例で必要とされるいくつかのフォーク/execよりもはるかに多いです。
この問題を解決するための代替案は何ですか?
答え1
pax
少なくともDebian、Suse、OpenBSD、NetBSDで見つかります。
find . -type f -name '*.pat' -print0 | pax -0rws'/?/_/gp' /path/to/dest/
pax
は標準ユーティリティ(同じtar
または逆cpio
)、-0
いくつかの実装で見つけることができますが、そのオプションはそうではありません。
?.pat
ファイルとファイルの両方が_.pat
存在する場合は、同じ名前に置き換えられるため、ターゲット上の1つが別のファイルを上書きします。同様に_
、ディレクトリがある場合、その内容はターゲットディレクトリ?
にマージされます。_
GNUsort
とGNUを使用すると、uniq
競合を事前に確認できます。
find . -type f -name '*.pat' -print0 |
tr '?' _ |
sort -z |
uniq -zd |
tr '\0' '\n'
競合するファイルを報告します(ディレクトリを除く)。
zsh
'を使用してzmv
競合を解決することはできますが、これはまだファイルごとにmkdir
1プラス1を意味します。cp
autoload zmv
mkdir-and-cp() {mkdir -p -- $3:h && cp $@}
zmv -n -Qp mkdir-and-cp '(**/)*.pat(D.)' '/path/to/dest/$f:gs/?/_/'
(幸せなら削除-n
)。
答え2
これはGNU tarを使用して達成できます。
$ find -type f -name '*.pat' -print0 | tar -c -f - --null --files-from - \
| tar -C /path/to/dest -v -x -f - --show-transformed --transform 's/?/_/g'
利点:
- 3つのフォーク/execのみが必要です(ファイル数に関係ありません)。
bash
ソースファイルの選択は非常に柔軟です。 (GNU)findのすべての機能を使用できます(たとえば、ksh93
allowedなどの単純なシェルワイルドカードよりも具体的である必要がある場合**
)。- 改行文字を含むファイル名にも安全です。
s/[^A-Za-z0-9 _-]/_/g
変換部分も非常に柔軟です。たとえば、文字クラスにないすべての文字を次のように置き換えることができます。[A-Za-z0-9 _-]
答え3
この問題を解決する最善の方法は、次のように定義することです。許可する文字を定義しようとするのではなく、文字を定義して転置をバイパスします。到着変えておく。許容される制限された文字セットを定義するよりも複雑です。これは、許可される文字セットに関する特定の規則があるためです。どこファイル名に。したがって、これは安全でなければならない限られたサブセットを使用します(FAT32文書を読んで知っている限り)。次のように動作する必要があります(bash4+)。
#!/bin/bash
shopt -s globstar nullglob
for file in **/*.pat; do
echo cp -t "${1:-.}" "${file//[![:alnum:].\-_\/]/_}"
done
これにより、次の内容が返されます。
$ > bar\?.pat
$ > baz\*.pat
$ > foo.pat
$ ./script foo
cp -t baz*.pat foo/baz_.pat
cp -t bar?.pat foo/bar_.pat
cp -t foo.pat foo/foo.pat
コピーするディレクトリを最初の引数として渡します。echo
出力された操作を続行するには、への呼び出しを削除してください。