Post History
It is incorrect for two reasons. 1. File names containing glob characters This is an edge case scenario. Consider this structure: . ├── abc ├── abc.part ├── cde └── c*e.part The outermos...
Answer
#2: Post edited
It is incorrect for several reasons.### You did not restrict Find's depthIt will thus descend into subdirectories and delete matching files there too.Easily solvable by adding `-maxdepth 1` to each Find.### File names containing [glob characters](https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html)- This is an edge case scenario.
- Consider this structure:
- ```
- .
- ├── abc
- ├── abc.part
- ├── cde
- └── c*e.part
- ```
- The outermost Find will find
- - `abc.part`, so `base=abc` and the innermost Find looks for files matching the glob `abc*`, which matches the `abc` file. Good.
- - `c*e.part`, so `base=c*e` and the innermost Find looks for files matching the glob `c*e*`, which matches the `cde` file. Bad, because `cde` does not contain `c*e`.
### File names with extra characters- If you have `abcde` and `abc.part` files, the former will be deleted because it matches `abc*` as should be clear from the previous case discussion.
- This particular problem would be easily fixed by changing `$base*` -> `$base.*`.
## SolutionsYou only want to operate in the current directory, so why notfor f in *.part; dorm "${f%.part}."*doneHere it is crucial **not** to quote the `*`, because we want it to act as a glob (and not literally).`${f%suffix}` is a special form of [parameter expansion](https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html) available in all POSIX shells. It means "remove `suffix` from the expansion of `f`". It looks cleaner than Basename and doesn't spawn an extra process.For something more along the lines you suggested,find . -maxdepth 1 -name '*.part' -exec sh -c 'rm "$(basename "$1" .part)".*' sh {} \;But it looks considerably convoluted and `-maxdepth 1` is not a [POSIX specified option to Find](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html), so you are also sacrificing portability. (It is possible to write the POSIX equivalent of `-maxdepth 1` but it adds obscurity.)
- It is incorrect for two reasons.
- ### 1. File names containing [glob characters](https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html)
- This is an edge case scenario.
- Consider this structure:
- ```
- .
- ├── abc
- ├── abc.part
- ├── cde
- └── c*e.part
- ```
- The outermost Find will find
- - `abc.part`, so `base=abc` and the innermost Find looks for files matching the glob `abc*`, which matches the `abc` file. Good.
- - `c*e.part`, so `base=c*e` and the innermost Find looks for files matching the glob `c*e*`, which matches the `cde` file. Bad, because `cde` does not contain `c*e`.
- ### 2. File names with extra characters
- If you have `abcde` and `abc.part` files, the former will be deleted because it matches `abc*` as should be clear from the previous case discussion.
- This particular problem would be easily fixed by changing `$base*` -> `$base.*`.
- ## Proposed solution
- Point 1 is the real challenge: It is quite involved to feed the file names back again into another Find's `-name` argument _and_ escape the meta-characters, which is always a mine field.
- I propose instead to use a shell with support for `**`, the recursive glob, for example Bash or Ksh with `globstar` option set or Zsh.
- #!/bin/bash
- shopt -s globstar #Not needed in Zsh
- for f in ./**/*.part; do
- rm ./**/"$(basename "$f" .part)".*
- done
- For a breakdown,
- - In line 2, **`**/*.part`** matches `./a.part` but also `./a/b/c.part` (hence "recursive glob").
- - In line 3, **`"$(basename "$f" .part)"`** removes all directory components of the file name and its `.part` extension. This would boil down to `a` and `c` in our example.
- So the full line **`rm ./**/"$(basename "$f" .part)".*`** recursively removes files matching the `a.*` and `c.*` patterns.
- It is crucial **not** to quote the `*` characters in the example, because we want it to act as a glob (and not to be parsed literally).
#1: Initial revision
It is incorrect for several reasons. ### You did not restrict Find's depth It will thus descend into subdirectories and delete matching files there too. Easily solvable by adding `-maxdepth 1` to each Find. ### File names containing [glob characters](https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html) This is an edge case scenario. Consider this structure: ``` . ├── abc ├── abc.part ├── cde └── c*e.part ``` The outermost Find will find - `abc.part`, so `base=abc` and the innermost Find looks for files matching the glob `abc*`, which matches the `abc` file. Good. - `c*e.part`, so `base=c*e` and the innermost Find looks for files matching the glob `c*e*`, which matches the `cde` file. Bad, because `cde` does not contain `c*e`. ### File names with extra characters If you have `abcde` and `abc.part` files, the former will be deleted because it matches `abc*` as should be clear from the previous case discussion. This particular problem would be easily fixed by changing `$base*` -> `$base.*`. ## Solutions You only want to operate in the current directory, so why not for f in *.part; do rm "${f%.part}."* done Here it is crucial **not** to quote the `*`, because we want it to act as a glob (and not literally). `${f%suffix}` is a special form of [parameter expansion](https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html) available in all POSIX shells. It means "remove `suffix` from the expansion of `f`". It looks cleaner than Basename and doesn't spawn an extra process. For something more along the lines you suggested, find . -maxdepth 1 -name '*.part' -exec sh -c 'rm "$(basename "$1" .part)".*' sh {} \; But it looks considerably convoluted and `-maxdepth 1` is not a [POSIX specified option to Find](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html), so you are also sacrificing portability. (It is possible to write the POSIX equivalent of `-maxdepth 1` but it adds obscurity.)