3

Im looking to copy the first 'n' files from one directory to another directory preferably with only cli tools (no scripts).

I've tried the following:

  • find . -maxdepth 1 -type f | head -5 | xargs cp -t /target/directory

    This looked promising, but failed because osx cp command doesn't appear to have the
    -t switch

  • exec in a few different configurations

    This probably failed for syntax problems on my end : /
    I couldn't seem to get a head type selection working

Any help or suggestions would be appreciated.

Thanks in advance!

4

You need the -J option with xargs.

find . -maxdepth 1 -type f | head -n5 | xargs -J X cp X /target/directory

The J option places all the filenames into the placeholder X, which can be any character(s) and cp accepts multiple files to a target directory. It can be visualized as-

cp file1 file2 file3 file4 file5 DESTINATION

EDIT:

To handle filenames with spaces, we have tr translate the newline character to the null character after each filename and then have xargs handle the null bit as a separator for the filenames.

 find . -maxdepth 1 -type f | head -n5 | tr '\n' '\0' | xargs -0 -J X cp -- X /target/directory
  • Excellent solution, just what I was after! Could you explain the 'n' in head -n5? At any rate, big thanks, and thanks @user3439894 for your input as well! – visyoual Sep 13 '18 at 17:21
  • @visyoual - From the manual: ` head [-n count | -c bytes] [file ...]` – fd0 Sep 13 '18 at 17:30
  • Ok. In my search for other solutions prior to posting this questions, several had the head command with -5 and not -n5. Wasn't sure if that was acceptable shorthand, or syntax error. I guess i'm still not sure. – visyoual Sep 13 '18 at 17:33
  • As you mentioned to me in a comment to my answer, your solution does not handle filenames with spaces, it there a way to achieve it modifying your command slightly? – user3439894 Sep 13 '18 at 17:37
  • 1
    @visyoual - depends on the version of head that you are using, With GNU head -n5 would mean the first five lines and -5 would mean all but the last five lines. With BSD head both are the same. – fd0 Sep 13 '18 at 17:45
1

I found a different solution without xargs or -exec but I think fd0's answer is a better way to go:

while IFS= read -r f; do cp "$f" "/target/directory/"; done < <(find . -maxdepth 1 -type f | head -n5)
  • 2
    I'll suggest the you set IFS to nothing-IFS= to handle any leading or trailing spaces in a filename and use process substitution instead of command substitution and a here string. Your solution will definitely be slower but it will handle filenames with spaces in them. My solution will not in its current form. – fd0 Sep 13 '18 at 16:38
  • @fd0, Good suggestion. I know with IFS= it would be IFS=; while read ... but not sure how to swap out the command substitution and here string for process substitution instead, without doing some research. Would not mind you showing me or even editing my answer, thanks. – user3439894 Sep 13 '18 at 16:57
  • Thanks @fd0, I missed the extra < in ... done < <( ... in my testing. :) – user3439894 Sep 13 '18 at 17:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .