2

Basically, I'm trying to increment each digit in _XtoX of each file below by 20

err_5JUP_N2_UUCCGU_1to2  err_5JUP_N2_UUCCGU_3to4  err_5JUP_N2_UUCCGU_5to6  err_5JUP_N2_UUCCGU_7to8  err_5JUP_N2_UUCCGU_9to10

such that the new file names should look like such:

err_5JUP_N2_UUCCGU_21to22  err_5JUP_N2_UUCCGU_23to24  err_5JUP_N2_UUCCGU_25to26  err_5JUP_N2_UUCCGU_27to28  err_5JUP_N2_UUCCGU_29to30

I came up with the following snippet of code:

for FILE in err*
do
    mv $FILE ${FILE/$"_"*to*/"_$((*+20))to$((*+20))}
done 

I essentially identified the pattern with "_"*to*/, but I'm pretty sure the * is not a reasonable way to capture the values and increment them. What is an alternative solution to this?

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
Pete Hwang
  • 23
  • 4
  • Look up the documentation for `BASH_REMATCH`, which is populated by `[[ $string =~ $regex ]]`. Ensure that all the content you want to reuse unchanged is included in match groups (distinct from the groups collecting content to use as input to calculate new values), and then it's easy to construct a new string from those groups. – Charles Duffy Jun 12 '23 at 15:22
  • Can you use the perl-based `rename` command? It can do this pretty easily using the `e` modifier in `s///`. This will be much easier than trying to do it using `bash` built-ins. – Barmar Jun 12 '23 at 15:28

1 Answers1

2

Try this Shellcheck-clean code:

#! /bin/bash -p

shopt -s extglob nullglob

for errfile in err*_+([0-9])to+([0-9]); do
    n1ton2=${errfile##*_}
    n1=${n1ton2%to*}
    n2=${n1ton2#*to}
    new_errfile=${errfile%_*}_$((n1+20))to$((n2+20))
    echo mv -v -- "$errfile" "$new_errfile"
done
  • Remove the echo if you are happy that the code will do what you want.
  • shopt -s ... enables some Bash settings that are required by the code:
    • extglob enables "extended globbing" (including patterns like +([0-9])). See the extglob section in glob - Greg's Wiki.
    • nullglob makes globs expand to nothing when nothing matches (otherwise they expand to the glob pattern itself, which is almost never useful in programs).
  • See Removing part of a string (BashFAQ/100 (How do I do string manipulation in bash?)) for explanations of ${var##pat}, ${var%pat}, and ${var#pat}.
  • Note that ALL_UPPERCASE variable names (like FILE) are best avoided because there is a danger of clashes with the large number of special ALL_UPPERCASE variables that are used in shell programming. See Correct Bash and shell script variable capitalization. That's why I used errfile instead of FILE.
pjh
  • 6,388
  • 2
  • 16
  • 17
  • First time I saw `-p` used in a shebang. Makes me wonder why Bash had to change the EUID by default. – konsolebox Jun 12 '23 at 16:04
  • @konsolebox, w.r.t. `-p` in the shebang, see the "Sanitizing the Environment in BASH" section of [Shell Script Security - Apple Developer](https://developer.apple.com/library/archive/documentation/OpenSource/Conceptual/ShellScripting/ShellScriptSecurity/ShellScriptSecurity.html). Essentially, it's to reduce the risk of the program being broken by stuff in the user's environment. – pjh Jun 12 '23 at 16:15
  • Thank you so much! I'm very much new to regular expressions and bash scripting. I appreciate your help :) – Pete Hwang Jun 12 '23 at 17:32
  • @pjh Can you cite the important text? I doubt it's the environment. Read the Bash manual. It's about the EUID. The purpose of `-p` is to not change the EUID to real UID which seems the default behavior of Bash. This default behavior is questionable, that's why I wondered. I wasn't really asking about the purpose of `-p` or why it's helpful to place it in a shebang. – konsolebox Jun 14 '23 at 03:50
  • @konsolebox, (some versions of) the Bash manual page have incomplete and/or misleading and/or incorrect information about the `-p` option. The relevant (and correct) text from the [Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html) is: "-p : Turn on privileged mode. In this mode, the `$BASH_ENV` and `$ENV` files are not processed, shell functions are not inherited from the environment, and the `SHELLOPTS`, `BASHOPTS`, `CDPATH` and `GLOBIGNORE` variables, if they appear in the environment, are ignored." – pjh Jun 14 '23 at 08:27
  • Or it can actually be both but no longer true for 5.2. The manual I read was from 5.1 and I no longer see the [text](https://superuser.com/a/532124) which describes the EUID behavior in the online version that has 5.2. – konsolebox Jun 15 '23 at 19:02
  • @konsolebox, the EUID manipulation is done only when running [setuid](https://en.wikipedia.org/wiki/Setuid), which is a relic of a bygone age. It's only of academic interest now. The important thing is that using the `-p` option reduces the risk of programs being broken by dangerous environment variable settings. – pjh Jun 15 '23 at 23:10
  • I knew the preservation/restriction of environment variables and the concept of why it's worth doing so. If it wasn't `-p` it would have been another option. Then again it wasn't what I was surprised/concerned about when I made the first comment so no need to further discuss on that. At the very least I'm happy to have discovered this inconsistent setuid behavior is already removed in 5.2. I just hope I remember again after a while not seeing the `-p` option. – konsolebox Jun 16 '23 at 08:29
  • @konsolebox, any change in Bash 5.2 is only in the documentation. Out of curiosity, I've tested the `-p` option and running setuid in multiple versions of Bash from version 3.2 to 5.2 (inclusive). The functionality is the same in all of them. Environment sanitization (e.g. ignoring exported functions) is done if the `-p` option is used and/or Bash is running setuid. Privilege dropping (setting the effective uid to be the same as the real uid) is done *only* if running setuid and the `-p` option is not used. – pjh Jun 30 '23 at 17:45