Linux replace in file name

Find and replace filename recursively in a directory

I want to rename all the files in a folder which starts with 123_xxx.txt to xxx.txt . For example, my directory has:

123_xxx.txt 123_yyy.txt 123_zzz.txt 

I have seen some useful bash scripts in this forum but I’m still confused how to use it for my requirement. Let us suppose I use:

for file in `find -name '123_*.txt'` ; do mv $file ; done 

15 Answers 15

find . -name '123_*.txt' -type f -exec sh -c ' for f; do mv "$f" "$/$" done' sh <> + 

No pipes, no reads, no chance of breaking on malformed filenames, no non-standard tools or features.

This is making use of parameter expansion to strip the basename from the end of the path, then append a new name which has had the prefix up to 123_ removed. The two operators to read about are % and ## , the rest is simple globbing.

find . -name "123*.txt" -exec rename 's/^123_//' <> ";" 

will do it. No AWK, no for, no xargs needed, but rename, a very useful command from the Perl lib. It is not always included with Linux, but is easy to install from the repos.

This is superior to the overkill awk and looping answers above for those people who have the perl rename .

To add to this, the use of find is unnecessary if you’re only talking about files in a single directory. It’s always best to keep it as simple as possible, so you can just do rename ‘s/^123_//’ * and it’ll do the exact same thing. Cheers.

@HodorTheCoder And if you’ve got globstar set in bash ( shopt -s globstar ), you can do it recursively: rename ‘s/^123_//’ **/*

In case you want to replace string in file name called foo to bar you can use this in linux ubuntu, change file type for your needs

find -name "*foo*.filetype" -exec rename 's/foo/bar/' <> ";" 

This really worked and is simple. How can I make this replace names of files in directories recursively.

@madu Find searches recursively into directories by default. If you wanted it to only search the current directory, you would add a -maxdepth 1 flag before the -name … flag.

you could check ‘rename’ tool

kent$ tree . |-- 123_a.txt |-- 123_b.txt |-- 123_c.txt |-- 123_d.txt |-- 123_e.txt `-- u |-- 123_a.txt |-- 123_b.txt |-- 123_c.txt |-- 123_d.txt `-- 123_e.txt 1 directory, 10 files kent$ find . -name '123_*.txt'|awk ''|sh kent$ tree . |-- a.txt |-- b.txt |-- c.txt |-- d.txt |-- e.txt `-- u |-- a.txt |-- b.txt |-- c.txt |-- d.txt `-- e.txt 1 directory, 10 files 

You should mention the homonym trap: you talk about special rename command that usurps the name of rename from util-linux (which does not deal with regular expressions).

The rename example isn’t recursive. The find example is, but it needlessly doesn’t use rename for renaming. And it pipes a generated script to sh , which is icky.

Читайте также:  Google earth no earth linux

Thanks, is it possible for me to modify the filename to all lower cases after the rename? i mean.. any Xxx.txt should be xxx.txt finally?

To expand on Sorpigal’s answer, if you want to replace the 123_ with hello_ , you could use

 find . -name '123*.txt' -type f -exec bash -c 'mv "$1" "$"' -- <> \; 

A slight variation on Kent’s that doesn’t require gawk and is a little bit more readable, (although, thats debatable..)

Using rename from util-linux 2.28.2 I had to use a different syntaxt:

find -name "*.txt" -exec rename -v "123_" "" <> ";" 

Provided you don’t have newlines in your filenames:

find -name '123_*.txt' | while IFS= read -r file; do mv "$file" "$"; done 

For a really safe way, provided your find supports the -print0 flag (GNU find does):

find -name '123_*.txt' -print0 | while IFS= read -r -d '' file; do mv "$file" "$"; done 

You can make a little bash script for that. Create a file named recursive_replace_filename with this content :

#!/bin/bash if test $# -lt 2; then echo "usage: `basename $0` " fi for file in `find . -name "*$1*" -type f`; do mv "'$file'" "$" done 
$ chmod +x recursive_replace_filename $ ./recursive_replace_filename 123_ "" 

Keep note that this script can be dangerous, be sure you know what it’s doing and in which folder you are executing it, and with which arguments. In this case, all files in the current folder, recursively, containing 123_ will be renamed.

Tried the answer above but it didn’t work for me cause i had the string inside folders and files name at the same time so here is what i did the following bash script:

 for fileType in d f do find -type $fileType -iname "stringToSearch*" |while read file do mv $file $( sed -r "s/stringToSearch/stringToReplaceWith/"  

First i began by replacing inside folders name then inside files name.

Worked well for me with spaces in the names of files. Just make sure you quote both vars in the mv line to work with spaces

Here's the only solution to date that works:

find-rename-recursive --pattern '123_' --string '' -- . -type f -name "123_*" 

All other solutions don't work for me--some even deleted files!

If the names are fixed you can visit each directory and perform the renaming in a subshell (to avoid changing the current directory) fairly simply. This is how I renamed a bunch of new_paths.json files each to paths.json :

for file in $(find root_directory -name new_paths.json) do (cd $(dirname $file) ; mv new_paths.json paths.json) done 

using the examples above, i used this to replace part of the file name. this was used to rename various data files in a directory, due to a vendor changing names.

find . -name 'o3_cloudmed_*.*' -type f -exec bash -c 'mv "$1" "$"' -- <> \;

Below, the find command searches recursively in the tree from the directory passed as first parameter (here it is the current directory "."). The following parameters are predicates to filter the files we are looking for. The "-name" predicate is a pattern to which the current file name must match (here we want manage only filenames beginning with '123_' and terminating with '.txt'. The "-type" filters the type of the file ('f' means regular file, 'd' would mean directory. ). The "-exec" predicate runs a command line into which '<>' is the current file name and the ';' terminates the command.

find . -name '123_*.txt' -type f -exec bash -c 'name=<>;mv $name $' \; 

Источник

Command line: search and replace in all filenames matched by grep

I'm trying to search and replace a string in all files matched by grep: grep -n 'foo' * will give me output in the form:

9 Answers 9

This appears to be what you want, based on the example you gave:

It is not recursive (it will not descend into subdirectories). For a nice solution replacing in selected files throughout a tree I would use find:

find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' <> \; 

The *.html is the expression that files must match, the .bak after the -i makes a copy of the original file, with a .bak extension (it can be any extension you like) and the g at the end of the sed expression tells sed to replace multiple copies on one line (rather than only the first one). The -print to find is a convenience to show which files were being matched. All this depends on the exact versions of these tools on your system.

A word of warning for cygwin users. find and sed combo seems to change the user rights for the files that are streamed through. This can be simply fixed by using the command chmod -R 644 * from the same dir level that was used when find/sed was operated.

A word of warning to the people that don't want to use the -i argument: if you don't use it, it doesn't work (don't ask me why)

@knocte -i tells sed to modify the file, otherwise it just prints the modified version to stdout. If you don't want the .bak file created, just omit the '.bak' part, -i works standalone too.

On OSX you need to give the find command a directory to start from, for example find . -name '*.html' or find directoryname/ -name '*' .

Do you mean search and replace a string in all files matched by grep?

perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *` 

Since this seems to be a fairly popular question thought I'd update.

Nowadays I mostly use ack-grep as it's more user-friendly. So the above command would be:

perl -p -i -e 's/old/new/g' `ack -l searchpattern` 

To handle whitespace in file names you can run:

ack --print0 -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g' 

you can do more with ack-grep . Say you want to restrict the search to HTML files only:

ack --print0 --html -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g' 

And if white space is not an issue it's even shorter:

perl -p -i -e 's/old/new/g' `ack -l --html searchpattern` perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files 

I think this might have a problem with files/folders that contain spaces. Can't open Untitled: No such file or directory, <> line 5 when trying "Untitled Folder/file.txt".

If your sed(1) has a -i option, then use it like this:

for i in *; do sed -i 's/foo/bar/' $i done 

If not, there are several ways variations on the following depending on which language you want to play with:

ruby -i.bak -pe 'sub(%r, 'bar')' * perl -pi.bak -e 's/foo/bar/' * 

I like and used the above solution or a system wide search and replace among thousands of files:

find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' <> \; 

I assume with the '*.htm?' instead of .html it searches and finds .htm and .html files alike.

I replace the .bak with the more system wide used tilde (~) to make clean up of backup files easier.

This works using grep without needing to use perl or find.

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @ 

xargs does not have a -i on OSX or BSD openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man1/… did you mean to use an upper case "I" ?

My xargs manpage (on Ubuntu) says that -i is deprecated, and to use -I instead. So, we should say: grep -rli 'old-word' * | xargs -I filepath sed -i 's/old-word/new-word/g' filepath

find . -type f -print0 | xargs -0 will process multiple space contained file names at once loading one interpreter per batch. Much faster.

@knocte, "cmd" is a token for the entire search and replace command for whichever given tool one chooses. This answer answers the question about how to deal with the white-space contained file names.

The answer already given of using find and sed

find -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' <> \;

is probably the standard answer. Or you could use perl -pi -e s/foo/bar/g' instead of the sed command.

For most quick uses, you may find the command rpl is easier to remember. Here is replacement (foo -> bar), recursively on all files in the current directory:

It's not available by default on most Linux distros but is quick to install ( apt-get install rpl or similar).

However, for tougher jobs that involve regular expressions and back substitution, or file renames as well as search-and-replace, the most general and powerful tool I'm aware of is repren, a small Python script I wrote a while back for some thornier renaming and refactoring tasks. The reasons you might prefer it are:

  • Support renaming of files as well as search-and-replace on file contents (including moving files between directories and creating new parent directories).
  • See changes before you commit to performing the search and replace.
  • Support regular expressions with back substitution, whole words, case insensitive, and case preserving (replace foo -> bar, Foo -> Bar, FOO -> BAR) modes.
  • Works with multiple replacements, including swaps (foo -> bar and bar -> foo) or sets of non-unique replacements (foo -> bar, f -> x).

Источник

Оцените статью
Adblock
detector