0

I would never do it myself but people with Windows machines insist on putting spaces in filenames.

I've composed this detailed command which works perfectly except for the files with spaces in them. Have tried everything, single quotes, double quotes, ticks, escape with backslash.

The command is supposed to copy everything from a directory with certain file extensions, except a list of files which I don't want copied. Unfortunately some of those files contain spaces. This is the command:

cp $(ls *.txt *.docx | grep --invert-match --fixed-strings \
-e not_this_one.txt \
-e not_this_one_either.docx \
-e "no not me.txt" \
-e "please leave me out as well.docx")  ./destination_directory/

Any ideas how to make this work?

2 Answers 2

3

Use find and xargs instead of $(...) parameter expansion:

find *.txt *.docx \( \
      -name not_this_one.txt \
      -o -name not_this_one_either.docx \
      -o -name 'no not me.txt' \
      -o -name "please leave me out as well.docx" \
    \) -prune -o -print0 |
  xargs -0 cp -t /tmp/destination_directory

We use the -prune option to exclude things we don't want to copy.

We use -print0 on the find command to produce NUL-terminated filenames, which when piped to xargs -0 correctly handles filenames that contain whitespace.

Finally, we use the -t <target_directory> option on cp because this allows xargs to just append a list of filenames to the command (without the -t, the destination directory needs to come last, which complicates things a bit).


Or, use tar:

tar -cf- \
    --exclude=not_this_one.txt \
    --exclude='not_this_one_either.docx' \
    --exclude='no not me.txt' \
    --exclude='please leave me out as well.docx' *.txt *.docx |
  tar -C /tmp/destination_directory -xf-

(And of course, you could put your list of exclude patterns into a file and use --exclude-from instead.)

6
  • 1
    Since you’re using cp -t, you could use find -exec directly instead of piping to xargs. Commented Jul 1, 2022 at 19:10
  • Yup. I just like xargs :). It's easy to write something using find -exec that will perform much more poorly than piping to xargs. Commented Jul 1, 2022 at 19:14
  • In this case, I doubt that -exec cp -t /tmp/destination_directory {} + would be slower than piping to xargs... Commented Jul 1, 2022 at 19:45
  • Yes, it works, only tested the first one. Also works with mv. I use the find command in simpler ways, and there's some useful stuff in there I've never seen before like cp -t. Still, I'm a beginner with bash, and next time I need to do something like this I'll try first to do it from python. Did not test the second command, but tar is more a compression and archiving command so not sure why you would use it for this. Commented Jul 1, 2022 at 20:46
  • 1
    @cardamom tar is very commonly used for bulk copying of files, just like this. Because it transforms your files into a stream, it's particularly useful when you need to copy over a network (e.g., from one computer to another, or from a container to the host). Commented Jul 1, 2022 at 23:29
1

Once you send ls *.txt *.docx tru a pipe | the shell is generating an string where files are not separated. Well, they could be separated by spaces (or newlines), which leads to confusion of the next tool that try to use such string splitting filenames on spaces (or newlines).

Since you are tagging the question with bash, the expansion of the globs *.txt and *.docx is done by the shell (bash), and bash is able to reject some file patterns given in GLOBIGNORE, you can simply do:

( GLOBIGNORE='not_this_one.txt:not_this_one_either.docx:no not me.txt:please leave me out as well.docx';
  echo \
  cp -t /tmp/destination_directory *.txt *.docx
)

The (...) here is to ensure that the setting of GLOBIGNORE is erased once the subshell is closed. Of course, you can remove it and do a unset GLOBIGNORE after. The echo is there to show you the result of the command. If you are happy with what you get, remove the echo.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.