Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

In directory /source I'd like to copy all files with a file name more than 15 characters to directory /dest. Is there a UNIX command to do this?

EDIT: Although this question explains how to search for a filename of a certain length, my question also asks how to copy the file.

share|improve this question
4  
    
I saw that one, but wasn't sure how to adapt it to copying. – Adam_G Jan 4 at 2:53
    
You mean you weren't sure how to adapt it to more than N characters instead of N characters ? Because, really, the fact you are searching, copying, removing etc is absolutely irrelevant. – don_crissti Jan 4 at 12:33
up vote 6 down vote accepted

You can make a pattern with 16-or-more characters and copy those files.

A simple (but not elegant) approach, using 16 ? characters, each matching any single character:

for n in /source/????????????????*;do [ -f "$n" ] && cp "$n" /dest/; done

After the 16th ?, use * to match any number of characters. The pattern might not really match anything, so a test -f to ensure it is a file is still needed.

share|improve this answer
2  
In bash, you could also use the nullglob shell option to avoid testing if any such file exists. – kojiro Jan 4 at 11:55

As far as I know, there is no command to do specifically that, but you can take advantage of shell glob patterns:

cp /source/????????????????* /dest

There are 16 question marks preceding the asterisk (you stated that you wanted "more than 15 characters" which means "at least 16").

Below are the caveats to my current answer:

  • Depending on your shell's configuration, this may not copy "hidden" files (ie files that starts with dot (.))
  • This will not copy any directories that are direct children of /source. It will simply emit a warning for each directory with a file name length of > 15 characters. If you want to recursively copy everything under /source and keep the hierarchy, that answer will be a little more involved.
share|improve this answer
    
Thank you. This works too – Adam_G Jan 4 at 0:05

The ????????????????????? solution is fine, unless you, like me, cannot count above eight. In such case you can use find:

find . -maxdepth 1 -regextype posix-egrep -regex '.{17,}' -exec cp -t /dest {} +
  • Notice that 17 should be used to take into account preceding ./.
  • -regextype posix-egrep is needed to allow braces {}pattern. Default regex type for find is emacs which doesn't allow that.
  • destination should be placed as a first cp argument (-t option) in order to treat all output from {} + as the sources.
share|improve this answer
    
(1) Taking the ./ into consideration, this finds all directories entries that are 15 characters long or longer.  The question says “more than 15 characters”, so you should use '.{18,}'.  (2) The question is vague on the precise requirements. As long as you’re using find anyway, it would be prudent to include -type f. – Scott Jan 4 at 13:21

If referring to a linux system with GNU, Gawk, then below is one solution. Source is /home/my_folder, copying to /dest for filenames longer than 7 characters assuming no filenames with spaces:

ls /home/my_folder/ |awk '{if(length($0)>7){print "cp /home/my_folder/"$0" /dest/"$0";";}}'

Which outputs:

cp /home/my_folder/orbit-antixcat /dest/orbit-antixcat;
cp /home/my_folder/spacefm-antixcat-569be3dc.tmp /dest/spacefm-antixcat-569be3dc.tmp;
cp /home/my_folder/xsession-modified-date /dest/xsession-modified-date;

If that looks OK then pipe through shell to actually copy the files:

ls /home/my_folder/ |awk '{if(length($0)>7){print "cp /home/my_folder/"$0" /dest/"$0";";}}'|bash

Can add some quotes to accommodate files with spaces in their names:

ls /home/my_folder |awk '{if(length($0)>7){print "cp \047/home/my_folder/"$0"\047 \047/dest/"$0"\047;";}}'|bash

Example output with quotes:

cp '/home/my_folder/kdecache-antixcat' '/dest/kdecache-antixcat';
cp '/home/my_folder/ksocket-antixcat' '/dest/ksocket-antixcat';

Can specify the cp binary if it happens to be aliased; perhaps there is a confirmation upon overwrite that you don't want.

ls /home/my_folder |awk '{if(length($0)>2){print "/bin/cp \047/home/my_folder/"$0"\047 \047/dest/"$0"\047;";}}'|bash

Above can be modified to copy directories if needed; currently they are omitted.

share|improve this answer
    
Thank you for all of the details. The really simple answer worked, as well. – Adam_G Jan 4 at 0:05
    
(1) Don’t parse the output from ls.  What if there are filenames containing single quotes (a.k.a. apostrophes)?  (2) You don’t need a semicolon (;) at the end of a command line.  (3) If you aren’t using bash features (you aren’t), it’s more portable (and possibly more efficient) to specify sh instead of bash. – Scott Jan 4 at 13:36
    
The ls parsing is usually something I do interactively but will keep risk in mind and avoid script usage. Concatenating shell commands with the semicolons can be of use for awk output; i.e., executing multiple commands per awk input. Thanks for the good information. – kph0x1 Jan 4 at 13:49

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.