How can I use xargs to copy files that have spaces

2019-01-09 22:23发布

I'm trying to copy a bunch of files below a directory and a number of the files have spaces and single-quotes in their names. When I try to string together find and grep with xargs, I get the following error:

find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote

Any suggestions for a more robust usage of xargs?

This is on Mac OS X 10.5.3 (Leopard) with BSD xargs.

21条回答
虎瘦雄心在
2楼-- · 2019-01-09 23:08

The easiest way to do what the original poster wants is to change the delimiter from any whitespace to just the end-of-line character like this:

find whatever ... | xargs -d "\n" cp -t /var/tmp
查看更多
淡お忘
3楼-- · 2019-01-09 23:09

I ran into the same problem. Here's how I solved it:

find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar

I used sed to substitute each line of input with the same line, but surrounded by double quotes. From the sed man page, "...An ampersand (``&'') appearing in the replacement is replaced by the string matching the RE..." -- in this case, .*, the entire line.

This solves the xargs: unterminated quote error.

查看更多
一夜七次
4楼-- · 2019-01-09 23:09

Here is a portable (POSIX) solution, i.e. one that doesn't require find, xargs or cp GNU specific extensions:

find . -name "*FooBar*" -exec sh -c 'cp -- "$@" ~/foo/bar' sh {} +

It will correctly handle files and directories with embedded spaces, newlines or whatever, and is more efficient (i.e. faster) than the accepted and most if not all of the other answers.

查看更多
Ridiculous、
5楼-- · 2019-01-09 23:10

bill_starr's Perl version won't work well for embedded newlines (only copes with spaces). For those on e.g. Solaris where you don't have the GNU tools, a more complete version might be (using sed)...

find -type f | sed 's/./\\&/g' | xargs grep string_to_find

adjust the find and grep arguments or other commands as you require, but the sed will fix your embedded newlines/spaces/tabs.

查看更多
Luminary・发光体
6楼-- · 2019-01-09 23:12

If you are using Bash, you can convert stdout to an array of lines by mapfile:

find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)

The benefits are:

  • It's built-in, so it's faster.
  • Execute the command with all file names in one time, so it's faster.
  • You can append other arguments to the file names. For cp, you can also:

    find . -name '*FooBar*' -exec cp -t ~/foobar -- {} +
    

    however, some commands don't have such feature.

The disadvantages:

  • Maybe not scale well if there are too many file names. (The limit? I don't know, but I had tested with 10 MB list file which includes 10000+ file names with no problem, under Debian)

Well... who knows if Bash is available on OS X?

查看更多
戒情不戒烟
7楼-- · 2019-01-09 23:14

For me, I was trying to do something a little different. I wanted to copy my .txt files into my tmp folder. The .txt filenames contain spaces and apostrophe characters. This worked on my Mac.

$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/'  | xargs -I{} cp -v {} ./tmp/
查看更多
登录 后发表回答