Ik I can use grep to find filenames with specific characters and then print that to a list but idk how to utilize said list to rename. I found another solution which’s using mv but couldn’t figure out how to make it work w subdirectories. I think using both grep and mv in unison’s the answer just idk how to do it. plz halp

    • Jo Miran@lemmy.ml
      link
      fedilink
      arrow-up
      3
      arrow-down
      1
      ·
      14 hours ago

      find dir1 – find something within directory “dir1”

      -type f – specifies that what you are looking fir is a file

      -name ‘crunk’ – the file name is ‘crunk’

      -exec bash -c – for the results, execute bash command

      The command executed is move (mv)

      ‘mv “$0” “${0/crunk/chunk}”’ {} ; – move from crunk to chunk

        • GaumBeist@lemmy.ml
          link
          fedilink
          arrow-up
          1
          ·
          edit-2
          9 hours ago

          Idk how versed you are on Bash Parameter Expansion or the find command, so I’d like to expand (pun intended) a little more on Jo Miran’s explanation (if you already know, I’ll leave this for others who may not):

          find’s -exec (and -execdir) option takes everything after it until a semicolon — which usually needs to be escaped, so the shell doesn’t accidentally treat it as the command separator special character — as a command to run and arguments to pass to that command. Furthermore, when using the -exec option, find treats all instances of {} as places where it should substitute the files it matched

          So breaking down -exec bash -c 'mv "$0" "${0/crunk/chunk}"' {} \; really just tells find to take the name of a file it found (in this example it would only match dir1/crunk) and put it after the command bash -c 'mv "$0" "${0/crunk/chunk}"'

          So now the command to run looks like bash -c 'mv "$0" "${0/crunk/chunk}"' dir1/crunk

          this command spawns a (sub)shell, bash, tells it to run the next argument as a command, -c, gives it that command to run, mv "$0" "${0/crunk/chunk}", and passes filename as an argument, dir1/crunk

          So now let’s talk shell parameters

          Usually $0 is a special parameter that references the shell (or script) that invoked the command. In the case of using the -c option, bash actually changes $0 to be the argument after the command to run: dir1/crunk

          So now the command looks more like

          mv "dir1/crunk" "${0/crunk/chunk}"

          So let’s finally get to the finish line: shell parameter expansion. Shell parameters (aka shell variables) can be written with curly braces around the name, so $SHELL and ${SHELL} refer to the same thing. But the curly braces can also let the shell know that if it sees certain special characters after the variable’s name, it should do some transformations to the contents of the variable.

          In this case ${0/crunk/chunk} takes the contents of $0, searches for the first instance of the string ”crunk", and replaces it with “chunk” before inserting it into the command.

          So now the final command to run looks like

          mv "dir1/crunk" "dir1/chunk”


          Also worth mentioning that the -name option of find accepts wildcards in its argument.

          I would also recommend using the -execdir option instead of -exec in this specific case, because it will run commands from inside the directories where it finds the files. In this case, that means {} would expand to ./crunk instead of dir1/crunk; this will be relevant in about 3 paragraphs.

          So now you can tweak the command to your needs. If you wanted to find more than one file that, for example, all had a “u” somewhere in the name, you could do so thusly

          find dir1 -name ”*u*"

          And then if you wanted to change the “r” in the filenames to “l”, you could do:

          find dir1 -name "*u*" -type f -execdir bash -c 'mv "$0" "${0/r/l}"' {} \;

          Note that you could not do this with the regular -exec option, as it would try to mv dir1/crunk dil1/crunk and throw an error because that directory (dil1) likely doesn’t exist… and even if it did, you don’t want your command moving files to different directories without your knowledge

          Notice that it also only changed the “r” in dir1 to an ”l”, and left the “r” in “crunk” alone? That’s not a typo on my part, that’s the intended behavior of Shell Parameter Expansion. If you wanted to replace all "r"s in filename, you would have to change the expression to ${0//r/l} (note the double slash)

          Seriously, it’s worth reading that page from gnu.org. Parameter expansion can get incredibly powerful, and it’s much easier to use the right format (${VAR/%r/h}) than trying to combine the most simple ones to achieve the same goal (e.g. DO NOT DO THIS: ${${VAR//r/h}/h/r}; it won’t even work as intended and it’s unnecessarily complex to read)