7

Is there any way to apply Bash variable substitution on find's output? I know I've seen someone do it on Stack Overflow but I can't seem to find that particular post anymore.

As an example, let's say I want to rename files ending in *.png to *_copy.png. I know I can do this using rename but it's just a thought experiment.

Now I'd like to be able to do something like this:

find . -name "*png" -exec mv "{}" "${{}%.*}_copy.png" \;

Which results in an invalid substitution. Of course, I could first assign the output to a variable and then apply substitution in a sub-shell, but is this really the only way?

find . -name "*.png" -exec bash -c 'var="{}";  mv "{}" "${var%.*}_copy.png"' \;

Or is there any way this can be achieved directly from {}?

Consensus

As Etan Reisner remarked, a better and safer way to handle the output of find would be to pass it as a positional argument:

find . -name "*.png" -exec bash -c 'mv "$0" "${0%.*}_copy.png"' "{}" \;
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
ShellFish
  • 4,351
  • 1
  • 20
  • 33
  • Editing an answer (or even a consensus summary) into the question is frowned on, as it makes that answer immune from the normal review, voting, and other community-based management. Consider adding a answer flagged "community wiki" if you want to have an answer that builds on others' work without being presented under your name / for your own rep gain. – Charles Duffy Jan 30 '18 at 00:27

1 Answers1

3

It took me a while to get the question. Basically you are asking if something like:

echo "${test.png%.png}"

could be used to get the word test.

The answer is no. The string manipulation operators starting with ${ are a subset of the parameter substitution operators. They work only on variables, not with string literals, meaning you need to store the string in a variable before. Like this:

img="test.png"
echo "${img%.png}"

Just for travellers from Google, I would use rename for this particular task. (As the OP already mentioned in his question). The command could look like this:

find -name '*png' -execdir rename 's/\.png/_copy.png/' {} +
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
  • Yes I know, hence I said " I know I can do this using rename but it's just a thought experiment.". The question is about the substitution not about the renaming. Thanks for the reply though. -- P.S. you may want to quote the `{}` in your answer to handle spaces. – ShellFish May 27 '15 at 11:58
  • Sorry, overlooked that. Let me add an example using bash. – hek2mgl May 27 '15 at 11:59
  • Don't worry about it, I overlook stuff all the time on stack overflow, haha. – ShellFish May 27 '15 at 12:00
  • @ShellFish You don't need to quote `{}` since it will not being subject of word splitting – hek2mgl May 27 '15 at 12:00
  • Oh, seriously? Wow I've quoted waaaay too many `{}` in my life then. Thanks for the tip! – ShellFish May 27 '15 at 12:01
  • (Note, that you indeed need to quote when you use `-exec bash -c ...` ) However, let me finsish the example first... – hek2mgl May 27 '15 at 12:02
  • I have no issue, it works using `bash -c`. The question is whether the substitution can be achieved **directly** on `{}`, without having to store in a variable first and executing it in a sub shell. -- Perhaps through some escape sequence magic or something. But saving to a variable first seems like a redundant step, I'd like to do it shorter. – ShellFish May 27 '15 at 12:09
  • No. The part after the `{` and before the `%` is supposed to be a variable name. http://tldp.org/LDP/abs/html/parameter-substitution.html#PARAMSUBREF – hek2mgl May 27 '15 at 12:11
  • If you include the fact that this can in no way be done in a single step, in your answer, I will accept. – ShellFish May 27 '15 at 12:15
  • Did it. Should be ok now. Isn't it? – hek2mgl May 27 '15 at 12:20
  • Yes, although I was hoping the answer was yes, haha. Anyway, thanks, +1 – ShellFish May 27 '15 at 12:21
  • 4
    As a side point you should **never** embed `{}` directly into a shell script being run with `sh -c` or similar. It isn't safe for spaces/etc. (and no quoting doesn't fix that it just moves the issue to quote escaping). You should always pass it as a positional argument to the script instead. – Etan Reisner May 27 '15 at 12:44
  • @EtanReisner Thanks a lot for sharing that, I will definitely take that into account for the future! – ShellFish May 27 '15 at 21:07