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.
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
How Would One Disable Nagle's Algorithm in Linux
How Find Out Which Process Is Using a File in Linux
Self Modifying Code Always Segmentation Faults on Linux
Nginx Redirect Url with Query Strings
Bash: Infinite Sleep (Infinite Blocking)
How to Attach a File Using Mail Command on Linux
How to Hide the Mouse Pointer Under Linux/X11
Write a Bash Shell Script That Consumes a Constant Amount of Ram for a User Defined Time
Can Docker Solve a Problem of Mismatched C Shared Libraries
Relinking an Anonymous (Unlinked But Open) File
How to Find Directory of Some Command
Difference Between Shell and Environment Variables
Install Mono and Monodevelop on Centos 5.X/6.X
How to Convert Yyyymmddhhmmss to a Date Readable by 'Date'
Counter Increment in Bash Loop Not Working
How to Change the Filename of a Shared Library After Building a Program That Depends on It
Linux Capabilities (Setcap) Seems to Disable Ld_Library_Path