Find a Pattern in Files and Rename Them

find a pattern in files and rename them

You are echo'ing your 'mv' command, not actually executing it. Change to:

find . -name '*-GHBAG-*' -exec bash -c 'mv $0 ${0/GHBAG/stream-agg}' {} \;

Rename multiple files by replacing a particular pattern in the filenames using a shell script

An example to help you get off the ground.

for f in *.jpg; do mv "$f" "$(echo "$f" | sed s/IMG/VACATION/)"; done

In this example, I am assuming that all your image files contain the string IMG and you want to replace IMG with VACATION.

The shell automatically evaluates *.jpg to all the matching files.

The second argument of mv (the new name of the file) is the output of the sed command that replaces IMG with VACATION.

If your filenames include whitespace pay careful attention to the "$f" notation. You need the double-quotes to preserve the whitespace.

How do I find a pattern and rename the file inserting a character between 2 parts of the matching pattern in the filename using prename?

You can use the unambiguous backreference syntax, ${n}, in the replacement pattern: ${1}0$2.

find . -type f -name '[[:digit:]] - [[:alpha:]]*' -execdir prename -n 's/(.*\/)(\d - \w.*)/${1}0$2/' {} +

Note you need no g modifier here, you only perform a single replacement.

Sample Image

Multiple file renaming based on regex pattern + renaming within files based on new file names

This solved it for me:

  • File renaming e.g. as described here: https://superuser.com/questions/16007/how-can-i-mass-rename-files
  • Batch replacement with this VS Code plugin: https://marketplace.visualstudio.com/items?itemName=angelomollame.batch-replacer
  • since I had the said table (old vs. new name) prepared, a simple regex replacement of the table entries did the trick to meet the prerequisites for the plugin, i. e. the old names were replaced by replace "old_file_name" and the new names by with "new_file_name"; then, just copy & paste everything for this plugin as described there and all is replaced

Find multiple files and rename them in Linux

You can use find to find all matching files recursively:

$ find . -iname "*dbg*" -exec rename _dbg.txt .txt '{}' \;

EDIT: what the '{}' and \; are?

The -exec argument makes find execute rename for every matching file found. '{}' will be replaced with the path name of the file. The last token, \; is there only to mark the end of the exec expression.

All that is described nicely in the man page for find:

 -exec utility [argument ...] ;
True if the program named utility returns a zero value as its
exit status. Optional arguments may be passed to the utility.
The expression must be terminated by a semicolon (``;''). If you
invoke find from a shell you may need to quote the semicolon if
the shell would otherwise treat it as a control operator. If the
string ``{}'' appears anywhere in the utility name or the argu-
ments it is replaced by the pathname of the current file.
Utility will be executed from the directory from which find was
executed. Utility and arguments are not subject to the further
expansion of shell patterns and constructs.

How to rename files using certain string inside each file?

You can use the following script to achieve your goal. Note, for the script to work on macOS, you either have to install GNU grep via Homebrew, or substitute the grep call with ggrep.

  • The script will search the current directory and all its subdirectories for *.html files.
  • It will substitute only the names of the files that contain the specific tag.
  • For multiple files that containt the same tag, each subsicuent file apart from the first will have an identifier appended to its name. E.g., 1_234.html, 1_234_1.html, 1_234_2.html
  • For files that contain multiple tags, the first tag encountered will be used.
#!/bin/bash

rename_file ()
{
# Check that file name received is an existing regular file
file_name="$(realpath "${1}")"
if [ ! -f "${file_name}" ]; then
echo "No argument or non existing file or non regular file provided"
exit 1
fi

# Get the tag number. If the number does not exist, the variable tag will be
# empty. The first tag on a file will be used if there are multiple tags
# within a file.
tag="$(grep -oP -m 1 '(?<=<div id="myID" style="display:none">).*?(?=</div>)' \
-- "${file_name}")"

# Rename the file only if it contained a tag
if [ -n "${tag}" ]; then
file_path="$(dirname "${file_name}")"

# Change directory to the file's location silently
pushd "${file_path}" > /dev/null || return

# Check for multiple occurences of files with the same tag
if [ -e "${tag}.html" ]; then
counter="$(find ./ -maxdepth 1 -type f -name "${tag}.html" -o -name "${tag}_*.html" | wc -l)"
tag="${tag}_${counter}"
fi

# Rename the file
mv "${file_name}" "${tag}.html"

# Return to previous directory silently
popd > /dev/null || return
fi

}

# Necessary in order to call rename_file from find command within main
export -f rename_file

# The entry point function of the script. This function searches for all the
# html files in the directory that the script is run, and all subdirectories.
# The function calls rename_files upon each of the found files.
main ()
{
find ./ -type f -name "*.html" -exec bash -c 'rename_file "${1}"' _ {} \;
}

main

Search for a string in a number of files, and then rename the files with that string

The errors you get are caused by two mistakes, one of them a classic off-by-one error. PowerShell arrays are zero-based, meaning that the last index of the array is one less than the number of its elements:

[ 'a', 'b', 'c' ]   → count == 3
0 1 2 → last index == 2 == 3-1

Thus your for loop may run while $i is less than $Files.Length (-lt), not less or equal (-le):

for ($i = 0; $i -lt $Files.Length; $i++) {

Also, you cannot use the same index variable for two different arrays ($Files and $AccountIDs) unless you made sure both arrays have the same length or at least that the second array ($AccountIDs) has more elements than the one used to determine the maximum index ($Files). If $AccountIDs has less elements than $Files your code will eventually attempt to access an index beyond the upper boundary of $AccountIDs. Besides, you probably want to check each file for all of the numbers from $AccountIDs anyway. Doing that requires a nested loop with a second index variable.


With that said, you're making this more complicated than it needs to be. You can simply put your IDs in a single regular expression and pipe the list of files into Select-String to check them against that regular expression:

$pattern = '\b(8155|8156|8428)\b'
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group.Matches[0].Groups[0].Value
$filename = $_.Group.Filename | Select-Object -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}

The regular expression \b(8155|8156|8428)\b matches any of the given numbers. The \b restrict the match to word boundaries to avoid matching numbers like 81552 or 842893 as well.

The Group-Object statement ensures the uniqueness of the renamed files (so that you don't attempt to rename a file more than once if more than one match is found in it).

.Matches[0].Groups[0].Value extracts the value of the first capturing group of the first match for each file.

The Select-Object -First 1 ensures that even if multiple matches were found in a file you have just one string with the filename, not an array of them.

Remove the -WhatIf switch once you verified that the rename operation would work correctly and re-run the whole statement to actually rename the files.


Edit: For PowerShell v2 you need to adjust the group handling a little bit, because that version doesn't support member enumeration.

Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group | Select-Object -Expand Matches -First 1 |
ForEach-Object { $_.Groups[0].Value }
$filename = $_.Group | Select-Object -Expand Filename -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}

Linux: find a match pattern and rename whole file with that Match

You can use perl-based rename:

rename 's/^(\d+).*jpg$/$1\.jpg/' *.jpg
  • ^ matches the position at the beginning of the input
  • \d+ matches one or more digit
  • .* matches everything in between
  • $ matches the position at the end of the input

The first group ($1) contains the digits you want to replace with.



Related Topics



Leave a reply



Submit