Skip to main content
added 286 characters in body
Source Link
mikeserv
  • 59.4k
  • 10
  • 122
  • 242

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:

  1. It is important that you lead your cp arguments 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 in cp'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 -- ./* or array=(./*).
  2. 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 shift only 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 5 case it will shift the 1 away, but in a set 1 2 3 case, it will shift nothing.
    • For example: a=(1 2 3); echo "${a[@]: -4}" prints a blank line.

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.

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:

  1. It is important that you lead your cp arguments 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 in cp'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 -- ./* or array=(./*).
  2. 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 shift only 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 5 case it will shift the 1 away, but in a set 1 2 3 case, it will shift nothing.
    • For example: a=(1 2 3); echo "${a[@]: -4}" prints a blank line.

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:

  1. It is important that you lead your cp arguments 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 in cp'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 -- ./* or array=(./*).
  2. 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 shift only 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 5 case it will shift the 1 away, but in a set 1 2 3 case, it will shift nothing.
    • For example: a=(1 2 3); echo "${a[@]: -4}" prints a blank line.

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.

deleted 16 characters in body
Source Link
mikeserv
  • 59.4k
  • 10
  • 122
  • 242

So long as you find the shell sort agreeable, you can just do:

set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$((($#>=4)*($#-4)))"
cp "$@" /path/to/target/dir

This is very similar to the bash-specific array solution pfferedoffered, but should be portable to any POSIX-compatible shell. Some notes about both methods:

  1. It is important that you lead your cp arguments 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 in cp'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 liklike... set -- ./* or array=(./*).
  2. 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 math multiplies 0 by the result of $#-4shift only happens if $# is less thanthere are at least 4 or 1 if greater than or equal to it,items in the arg array - and then shiftsonly shifts away the resulting countthose first args that make a surplus of leading arguments4..

    • So in a set 1 2 3 4 5 case it will shift the 1 away, but in a set 1 2 3 case, it will shift nothing.
    • For example: a=(1 2 3); echo "${a[@]: -4}" prints a blank line.

So long as you find the shell sort agreeable, you can just do:

set -- /path/to/source/dir/*
shift "$((($#>=4)*($#-4)))"
cp "$@" /path/to/target/dir

This is very similar to the bash-specific array solution pffered, but should be portable to any POSIX-compatible shell. Some notes about both methods:

  1. It is important that you lead your cp arguments 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 in cp'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 lik. set -- ./* or array=(./*).
  2. 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 math multiplies 0 by the result of $#-4 if $# is less than 4 or 1 if greater than or equal to it, and then shifts away the resulting count of leading arguments.

    • So in a set 1 2 3 4 5 case it will shift the 1 away, but in a set 1 2 3 case, it will shift nothing.
    • For example: a=(1 2 3); echo "${a[@]: -4}" prints a blank line.

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:

  1. It is important that you lead your cp arguments 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 in cp'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 -- ./* or array=(./*).
  2. 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 shift only 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 5 case it will shift the 1 away, but in a set 1 2 3 case, it will shift nothing.
    • For example: a=(1 2 3); echo "${a[@]: -4}" prints a blank line.
added 143 characters in body
Source Link
mikeserv
  • 59.4k
  • 10
  • 122
  • 242

So long as you find the shell sort agreeable, you can just do:

set -- /path/to/source/dir/*
shift "$((($#>=4)*($#-4)))"
cp "$@" /path/to/target/dir

This is very similar to the bash-specific array solution pffered, but should be portable to any POSIX-compatible shell. Some notes about both methods:

  1. It is important that you lead your cp arguments 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 in cp'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 lik. set -- ./* or array=(./*).
  2. 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 math multiplies 0 by the result of $#-4 if $# is less than 4 or 1 if greater than or equal to it, and then shifts away the resulting count of leading arguments.

    • So in a set 1 2 3 4 5 case it will shift the 1 away, but in a set 1 2 3 case, it will shift nothing.
    • For example: a=(1 2 3); echo "${a[@]: -4}" prints a blank line.

So long as you find the shell sort agreeable, you can just do:

set -- /path/to/source/dir/*
shift "$((($#>=4)*($#-4)))"
cp "$@" /path/to/target/dir

This is very similar to the bash-specific array solution pffered, but should be portable to any POSIX-compatible shell. Some notes about both methods:

  1. It is important that you lead your cp arguments 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 in cp'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 lik. set -- ./* or array=(./*).
  2. 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.

So long as you find the shell sort agreeable, you can just do:

set -- /path/to/source/dir/*
shift "$((($#>=4)*($#-4)))"
cp "$@" /path/to/target/dir

This is very similar to the bash-specific array solution pffered, but should be portable to any POSIX-compatible shell. Some notes about both methods:

  1. It is important that you lead your cp arguments 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 in cp'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 lik. set -- ./* or array=(./*).
  2. 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 math multiplies 0 by the result of $#-4 if $# is less than 4 or 1 if greater than or equal to it, and then shifts away the resulting count of leading arguments.

    • So in a set 1 2 3 4 5 case it will shift the 1 away, but in a set 1 2 3 case, it will shift nothing.
    • For example: a=(1 2 3); echo "${a[@]: -4}" prints a blank line.
Source Link
mikeserv
  • 59.4k
  • 10
  • 122
  • 242
Loading