0

when I change any configuration I made a copy of the original file with suffix .original. Now I am composing a simple script that will find all *.original files and work with both versions, ie with and without .original suffix.

I do use command locate with regular expression to avoid mismatched files with the original string in the middle of the path.

My command is locate --regex ^.*\\.original$ that works perfectly in terminal. It finds all files that ends with the .original suffix such as file.original

However when I use the same command in bash script in the for loop it returns me variants such as file.original file-original file_original etc.

How should I modify the regex or for loop to get only .original files?

My shell is:

$ echo $BASH
/usr/bin/bash

My bash script is:

#!/usr/bin/bash
echo "Plain locate command:"
locate --regex ^.*\\.original$ | grep test

echo "For loop:" for file in locate --regex ^.*\\.original$ | grep test; do echo $file done

You can use this for testing:

mkdir /tmp/test 
touch /tmp/test/file.original
touch /tmp/test/file-original
touch /tmp/test/file_original
updatedb
locate --regex ^.*\\.original$

In my terminal it will find:

/tmp/test/file.original

But my script will find:

Plain locate command:
/tmp/test/file.original
For loop:
/tmp/test/file-original
/tmp/test/file.original
/tmp/test/file_original
ino
  • 317

1 Answers1

4

Backticks require more escaping. This is one of the reasons you should prefer $() (see e.g. "backslash fun" in this answer: What's the difference between $(stuff) and `stuff`?).

Due to not enough escaping inside the backticks, the dot you wanted to escape is unescaped when the string is ultimately interpreted as regex.

Change the relevant line to:

for file in $(locate --regex '^.*\.original$' | grep test); do

Notes:

  • I used single-quoting to conveniently handle the backslash and the asterisk. In your original code the unquoted and unescaped asterisk can trigger the filename expansion.

  • ^.* in the regex changes nothing. Single-quoted \.original$ would be enough. I decided to keep ^.* only to point out the possibility of filename expansion.

  • Get used to quoting variables (e.g. echo "$file"). The linked answer recommends quoting $() as well, but in our for loop quoting would be wrong. Not quoting $() is also wrong. Both are wrong because…

  • In general building for loops like this is often wrong. It's the Bash pitfall number one. To do something with the output of locate better pipe it to xargs, preferably as null-terminated strings:

    locate -0 … | grep -z … | xargs -r0 …
    

    These options are not portable.

  • Thank you for the solution and for the valuable notes! There is still too much to learn... ;) – ino May 24 '21 at 12:46