1

I have following directory structure:

├── file1.json ├── file2.json └── file3.json 

I want to remove the .json suffix. I know the best way would probably be the rename command or to use a for-loop, but I was wondering if the find command can do that too.

For example appending .json is simple, I following command:

find . -type f -name 'file?' -exec mv {} {}.json \; 

But now I want to reverse the name process, how can I do that with find?

3
  • @ChrisDavies oh I don't have any restrictions. I also have no goals to achieve. This is only for learning purposes. In the real world I would use rename. But I was thinking about find and if I can subtstring the {} var :) It doesn't even matter if I use mv or echo or any other command. That was just the first best example I could come up with^^ Commented Oct 26, 2023 at 12:20
  • @don_crissti Yeah I know^^ And yeah I have seen Manipulate {} return string from find -exec and many other posts. But most of them use a for-loop. I don't know, I just have the feeling that there has to be a better way to do it^^ Commented Oct 26, 2023 at 12:21
  • 1
    @Pixelbog, you don't have to use a loop, but you do have to use a shell or something to modify the filename. find can't do it itself. Using {} + with a loop in the shell just optimizes it by starting fewer shell processes. Commented Oct 26, 2023 at 13:11

2 Answers 2

2

Your initial process renamed files by appending .json. For example file1 (matching the pattern file?) was renamed to file1.json. The reverse is more complicated and requires a little shell script processing

find . -type f -name 'file?.json' -exec sh -c 'mv -- "$1" "${1%.json}"' _ {} \; 

This will invoke sh for each file found by find. Typically a more efficient process can be created for very little increase in complexity:

find . -type f -name 'file?.json' -exec sh -c 'for f in "$@"; do mv -- "$f" "${f%.json}"; done' _ {} + 

Both solutions make use the POSIX shell substitution ${var%suffix}, which returns a value corresponding to $var with the literal text suffix removed (if possible) from the end of its value.

var=hello.txt echo "${var%.txt}" # "hello" because ".txt" is removed echo "${var%.zzz}" # "hello.txt" because there is no ".zzz" suffix echo "$var" # "hello.txt" because the variable itself is unchanged 
2

Well, the manual is quite clear:

The string ‘{}' is replaced by the current file name being processed [...]

find does not provide additional placeholders that would manipulate the file name like gnu parallel does with its {.}, {/} etc... however, there is an alternative to find which does: it's called fd (sometimes fdfind) and if you used that one you could run:

fd -e json -x mv -- {} {.} 

fd -e json means find files with json extension, -x stands for "execute" then {} and {.} are placeholders for file name and respectively file name without extension (same string replacements as gnu parallel).

Note that you'll need to quote {} (as '{}') in rc, akanga and older versions of fish, and {.} in those plus csh, tcsh, zsh -o braceccl, where {/} have a special meaning.

Also beware that by default fd (contrary to find) ignores hidden files (those whose name starts with a . or are below a directory whose name starts with a . but also those mentioned in .ignore/.gitignore/.git/ignore/.fdignore... files; see the -H/--hidden, -I/--no-ignore, -u/--unrestricted options to change that) and those whose name cannot be decoded as UTF-8 (regardless of the locale).

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.