So long as you find the shell sort agreeable, you can just do:
set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
cp "$@" /path/to/target/dir
This is very similar to the bash-specific array solution offered, but should be portable to any POSIX-compatible shell. Some notes about both methods:
It is important that you lead your
cparguments w/cp --or you get one of either.a dot or/at the head of each name. If you fail to do this you risk a leading-dash incp's first argument which can be interpreted as an option and cause the operation to fail or to otherwise render unintended results.- Even if working with the current directory as the source directory this is easily done like...
set -- ./*orarray=(./*).
- Even if working with the current directory as the source directory this is easily done like...
It is important when using both methods to ensure you have at least as many items in your arg array as you attempt to remove - I do that here with a math expansion. The
shiftonly happens if there are at least 4 items in the arg array - and then only shifts away those first args that make a surplus of 4..- So in a
set 1 2 3 4 5case it will shift the1away, but in aset 1 2 3case, it will shift nothing. - For example:
a=(1 2 3); echo "${a[@]: -4}"prints a blank line.
- So in a
If you are copying from one directory to another, you can use pax:
set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
pax -rws '|.*/|new_prefix|' "$@" /path/to/target/dir
...which would apply a sed-style substitution to all filenames as it copies them.