4
\$\begingroup\$

I have written the following script to automate my frequent usage of rename. This saves me from always typing the whole command to the command line as well as doing a dry run with -n for testing before applying the pattern.

#!/bin/bash
 if [ ! -d ~/.script-history ]; then
 echo -e "Folder .script-history does not exist and gets created... \n";
 mkdir ~/.script-history\
 && { echo -e "created folder: ~/.script-history \n"; }\
 || { echo -e "could not create folder: ~/.script-history \n"; }
 fi
 history -r ~/.script-history/rename
 set -o vi
 search=""
 replace=""
 echo -e "Please enter token to be replaced: "
 read -e -r search
 history -s "$search"
 echo -e "Please enter replacement: "
 read -e -r replace
 history -s "$replace"
 history -w ~/.script-history/rename
 value=$(rename -n "s/$search/$replace/g" ./*)
 echo -e "$value"
 lines=$(echo -en "$value" | wc -m)
 if [ "$lines" -gt 0 ]; then
 read -p "Do you want to apply changes? [Y]es [n]o " yn
 case $yn in
 [yY]* ) echo "renaming files...";\
 rename "s/$search/$replace/g" ./*\
 && { echo "success :-)"; }\
 || { echo "failed :-("; };;
 [nN]* ) echo "no changes have been made."; exit;;
 * ) echo "Are you kidding me???";;
 esac
 elif [ "$lines" -eq 0 ]; then
 echo -e "No matches found for <$search> !"
 fi
  • For making it easier to adjust or reuse the changes done with the script, I added a history, while the first part checks if the folder exists where I save my histories for script files. I do not quite understand this line set -o vi but it is working.
  • For me as a beginner it was quite tricky to get the script recognizing whether there are changes to be applied or not. My workaround here is the $lines variable, where I count the output of chars and then process with "$lines" -gt 0 or "$lines" -eq 0.

One of the questions I have is about the usage of the A && B || C pipe. I use it quite frequently in my scripts, where ShellCheck reports the following:

SC2015: Note that A && B || C is not if-then-else. C may run when A is true.

The other output of ShellCheck is:

SC2162: read without -r will mangle backslashes.

I think it is ok, since the user is only expected to input YyNn. While I use the script on a daily basis, is the coding sufficient or where can it be improved?

The disadvantage of the script is that you have to type in both search and replacement before it checks if there are matches. An improvement could be to add a function that checks for that and returns to the search input when there are no matches.

janos
113k15 gold badges154 silver badges396 bronze badges
asked Jul 29, 2017 at 15:15
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Checking if a value is not empty

This is a very complicated way to check that value is not empty:

lines=$(echo -en "$value" | wc -m)
if [ "$lines" -gt 0 ]; then

This is equivalent:

if [ "$value" ]; then

Avoid echo -e

It's good to avoid all flags of echo, such as -e, because they are not portable. Instead of this:

 echo -e "Folder .script-history does not exist and gets created... \n";

I recommend to add a second empty echo:

echo "Folder .script-history does not exist and gets created..."
echo

The concern about A && B || C

Shellcheck raised an issue here:

 rename "s/$search/$replace/g" ./*\
 && { echo "success :-)"; }\
 || { echo "failed :-("; };;

The issue is that if rename succeeds but then echo success fails, the || will get executed. You can ignore this issue, because in the unlikely event that the first echo fails, the second (after the ||) is likely to fail too, so it won't matter. If you want to be pedantic/bullet-proof, then you have to spell out a proper if-else statement instead of the && ... || chaining.

In any case, the command can be simplified without the grouping:

rename "s/$search/$replace/g" ./* \
&& echo "success :-)" \
|| echo "failed :-(" ;;

I also added spaces in front of the \ before the line breaks, to make it perfectly clear that the argument before the \ is not being continued on the next line. Without that, this point would be ambiguous, making the script a bit harder to read.

The concern about read without -r

If the user input contains \, it may not get preserved. That's hardly ever intended, and probably you would not want that either. As a rule of thumb, it's good to always use read -r.

answered Jul 30, 2017 at 8:17
\$\endgroup\$
5
  • \$\begingroup\$ Thanks allot for your detailed feedback. The use of the history is when you run the script multiple times you can get to the old input via the arrow up key. So lets say you renamed xx1 to file, then you decided you want to rename file to audio then you can just select the file with the arrow up key and so on. I found that pretty useful. If I would just append the commands I used to a file, how could I call them so they could be reused in the input? \$\endgroup\$ Commented Jul 30, 2017 at 16:44
  • \$\begingroup\$ @nath I see your point. I removed that section. \$\endgroup\$ Commented Jul 30, 2017 at 16:49
  • \$\begingroup\$ how would you go with the elif [ "$lines" -eq 0 ]; then if there was no use of the $lines variable anymore? \$\endgroup\$ Commented Jul 30, 2017 at 17:05
  • \$\begingroup\$ @nath value is either empty or not. So you don't need an elif, use a simple else instead \$\endgroup\$ Commented Jul 30, 2017 at 17:07
  • \$\begingroup\$ I just tried: elif [ ! "$value" ]; then would work too, but thanks again, I'm going to use your suggestion withelse simplifying the code! \$\endgroup\$ Commented Jul 30, 2017 at 17:11

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.