- Rename Files and Directories (Add Prefix)
- How to copy and add prefix to file names in one step?
- **for those with spaces in their filenames:
- Rename files in the local folder adding a prefix or suffix
- Add a prefix string to beginning of each line
- 18 Answers 18
- Add prefix to all images (recursive)
- 7 Answers 7
Rename Files and Directories (Add Prefix)
Thanks to Peter van der Heijden, here’s one that’ll work for filenames with spaces in them:
for f in * ; do mv -- "$f" "PRE_$f" ; done
(«—» is needed to succeed with files that begin with dashes, whose names would otherwise be interpreted as switches for the mv command)
If you change the ls command to * and put double quotes around the arguments to mv , it will work for files containing spaces.
It should be «mv —» not just «mv», to guard against files starting with dashes, that would otherwise be interpreted as command switches. I will contribute an edit to correct this.
There are no problems with metacharacters or whitespace in filenames.
Some systems have a util-linux utility called «rename» which works differently (on Ubuntu it’s called «rename.ul»).
For adding prefix or suffix for files(directories), you could use the simple and powerful way by xargs:
ls | xargs -I <> mv <> PRE_<> ls | xargs -I <> mv <> <>_SUF
It is using the paramerter-replacing option of xargs: -I. And you can get more detail from the man page.
P.S.: If you just want to rename part files (directories) of current directory, just filter it before xargs, such as: ls *.old | xargs -I <> mv <> PRE_<>
interestingly using rename did not work. using xargs went well for my RHEL setup so +1 for this option, makes it easy to understand the command
Doesn’t work if filename contains single quote, fails with: xargs: unmatched single quote; by default quotes are special to xargs unless you use the -0 option
This could be done running a simple find command:
find * -maxdepth 0 -exec mv <> PRE_<> \;
The above command will prefix all files and folders in the current directory with PRE_ .
To add a prefix to all files and folders in the current directory using util-linux’s rename (as opposed to prename , the perl variant from Debian and certain other systems), you can do:
This finds the first occurrence of the empty string (which is found immediately) and then replaces that occurrence with your prefix, then glues on the rest of the file name to the end of that. Done.
perl -e 'rename $_, "PRE_$_" for '
On my system, I don’t have the rename command. Here is a simple one liner. It finds all the HTML files recursively and adds prefix_ in front of their names:
for f in $(find . -name '*.html'); do mv "$f" "$(dirname "$f")/prefix_$(basename "$f")"; done
Here is a simple script that you can use. I like using the non-standard module File::chdir to handle managing cd operations, so to use this script as-is you will need to install it ( sudo cpan File::chdir ).
#!/usr/bin/perl use strict; use warnings; use File::Copy; use File::chdir; # allows cd-ing by use of $CWD, much easier but needs CPAN module die "Usage: $0 dir prefix" unless (@ARGV >= 2); my ($dir, $pre) = @ARGV; opendir(my $dir_handle, $dir) or die "Cannot open directory $dir"; my @files = readdir($dir_handle); close($dir_handle); $CWD = $dir; # cd to the directory, needs File::chdir foreach my $file (@files) < next if ($file =~ /^\.+$/); # avoid folders . and .. next if ($0 =~ /$file/); # avoid moving this script if it is in the directory move($file, $pre . $file) or warn "Cannot rename file $file: $!"; >
How to copy and add prefix to file names in one step?
I often add the -v option to cp to allow me to watch the progress.
You can use shell globbing:
for f in *.c; do cp -- "$f" "$OTHERDIR/old#$f"; done
The for variable in GLOB format will expand the glob to all matching files/directories (excluding hidden-ones) and iterate over them, saving each in turn as $variable (in the example above, $f ). So, the command I show will iterate over all non-hidden files, copying them and adding the prefix.
If I do for f in /foo/bar/*.c , $f will contain the entire path, instead only the name of the file. How can I obtain only the name of the file?
@alexandernst either cd /foo/bar/; for f in *c , or use for f in /foo/bar/*.c; do name=$ . or name=$(basename «$f») .
Here is a one-liner using xargs , tr and globbing. Might have some value.
echo *.c | tr ' ' '\n' | xargs -n1 -I<> cp "<>" "PREFIX<>"
This returns all files matching *.c as a space-separated string. Next, tr turns the extra spaces into newlines (N.B. did not test file names with spaces**). Then, xargs gets populated with each file name, and runs cp with the appropriate name and prefix.
*.c can be modified for other, useful globs. Other prefixes in the xargs and cp part can be used as well.
**for those with spaces in their filenames:
(requires find that supports -print0 ) Similar to above, we can use find to output a null-seperated list of files, and tweak xargs with a flag to separate on null
find . -name '*.c' -print0 | xargs -0 -n1 -I<> cp "<>" "PREFIX<>"
Rename files in the local folder adding a prefix or suffix
That’s almost perfect! It would be perfect if it (1) checked if the prefix or suffix is already present (and then skipped), and (2) worked on files with no extension. Sorry for bothering so much.
For (1) you could (I think, not tested) add the prefix check by wrapping the mv in an if [ $f == $prefix* ] and you can change the suffix one with if [ $f == *$suffix$([ ! -z $extension ] && echo -n ‘.’)$extension ] around the couple mv commands in addsuffix. For (2) it should already work on files with no extension, unless I am a horrible programmer.
There’s a Perl rename command that should do the trick. Beware though: there are several different commands called rename , so make sure you have got the Perl script that expects a perlexpr as its argument. This is the rename command provided by the perl package on Debian and derivatives (Ubuntu, Mint, …), but not on other distributions which may have a different utility called rename .
martin@martin ~/test % touch a.txt martin@martin ~/test % touch b.txt martin@martin ~/test % rename 's/^(prefix)?/prefix/' *.txt martin@martin ~/test % ll insgesamt 0 -rw-rw-r-- 1 martin martin 0 Sep 19 23:56 prefixa.txt -rw-rw-r-- 1 martin martin 0 Sep 19 23:56 prefixb.txt martin@martin ~/test % rename 's/^(prefix)?/prefix/' *.txt martin@martin ~/test % ll insgesamt 0 -rw-rw-r-- 1 martin martin 0 Sep 19 23:56 prefixa.txt -rw-rw-r-- 1 martin martin 0 Sep 19 23:56 prefixb.txt
As you can see, it’s idempotent — it won’t add the prefix again when you call it multiple times.
It works by either replacing the (zero-length) start of the strings ( ^ ) or the start followed by an optional prefix string with prefix .
I’ll leave encapsulating this in either a shell script or a shell function as an exercise for you 🙂
Postfix is a bit harder though, because you have to figure out what part of the filename constitutes the extension.
Add a prefix string to beginning of each line
I could write a Ruby script, but it is better if I do not need to. prefix will contain / . It is a path, /opt/workdir/ for example.
18 Answers 18
# If you want to edit the file in-place sed -i -e 's/^/prefix/' file # If you want to create a new file sed -e 's/^/prefix/' file > file.new
If prefix contains / , you can use any other character not in prefix , or escape the / , so the sed command becomes
's#^#/opt/workdir#' # or 's/^/\/opt\/workdir/'
@benjamin, I had already upvoted your answer, however, I prefer sed for lightweight tasks such as this. If «prefix» is known, it’s very easy to pick a character not from «prefix».
awk '$0="prefix"$0' file > new_file
In awk the default action is » (i.e. print the whole line), so the above is equivalent to:
With Perl (in place replacement):
With a large file (12 G), awk reports awk: out of memory in readrec 1 source line number 1 , but the solution with sed completes successfully.
This is the best answer, with AWK it Worked right off the bat without having to annoyingly deal with escaping regex special characters
You can use Vim in Ex mode:
For me, I just open the file in vim and type :%s/^/prefix/ , since this strategy ends up being useful in many situations
If your prefix is a bit complicated, just put it in a variable:
Then, you pass that variable and let awk deal with it:
awk -v prefix="$prefix" '' input_file.txt
Here is a hightly readable oneliner solution using the ts command from moreutils
$ cat file | ts prefix | tr -d ' '
And how it’s derived step by step:
# Step 0. create the file $ cat file line1 line2 line3
# Step 1. add prefix to the beginning of each line $ cat file | ts prefix prefix line1 prefix line2 prefix line3
# Step 2. remove spaces in the middle $ cat file | ts prefix | tr -d ' ' prefixline1 prefixline2 prefixline3
‘ts’ is not installed by default on many Linux distros. Also, downvoting because the trailing «tr -d ‘ ‘» in this answer will remove all spaces from the lines, not just the space that was added by ‘ts’
perl -pe 's/^/PREFIX/' input.file
Using & (the whole part of the input that was matched by the pattern”):
cat in.txt | sed -e "s/.*/prefix&/" > out.txt
cat in.txt | sed -e "s/\(.*\)/prefix\1/" > out.txt
#!/bin/bash prefix="something" file="file" while read -r line do echo "$$line" done newfile mv newfile $file
While I don’t think pierr had this concern, I needed a solution that would not delay output from the live «tail» of a file, since I wanted to monitor several alert logs simultaneously, prefixing each line with the name of its respective log.
Unfortunately, sed, cut, etc. introduced too much buffering and kept me from seeing the most current lines. Steven Penny’s suggestion to use the -s option of nl was intriguing, and testing proved that it did not introduce the unwanted buffering that concerned me.
There were a couple of problems with using nl , though, related to the desire to strip out the unwanted line numbers (even if you don’t care about the aesthetics of it, there may be cases where using the extra columns would be undesirable). First, using «cut» to strip out the numbers re-introduces the buffering problem, so it wrecks the solution. Second, using «-w1» doesn’t help, since this does NOT restrict the line number to a single column — it just gets wider as more digits are needed.
It isn’t pretty if you want to capture this elsewhere, but since that’s exactly what I didn’t need to do (everything was being written to log files already, I just wanted to watch several at once in real time), the best way to lose the line numbers and have only my prefix was to start the -s string with a carriage return (CR or ^M or Ctrl-M). So for example:
#!/bin/ksh # Monitor the widget, framas, and dweezil # log files until the operator hits # to end monitoring. PGRP=$$ for LOGFILE in widget framas dweezil do ( tail -f $LOGFILE 2>&1 | nl -s"^M$> " ) & sleep 1 done read KILLEM kill -- -$
Add prefix to all images (recursive)
I have a folder with more than 5000 images, all with JPG extension. What i want to do, is to add recursively the «thumb_» prefix to all images. I found a similar question: Rename Files and Directories (Add Prefix) but i only want to add the prefix to files with the JPG extension.
7 Answers 7
One of possibly solutions:
find . -name '*.jpg' -printf "'%p' '%h/thumb_%f'\n" | xargs -n2 echo mv
Principe: find all needed files, and prepare arguments for the standard mv command.
- arguments for the mv are surrounded by ‘ for allowing spaces in filenames.
- The drawback is: this will not works with filenames what are containing ‘ apostrophe itself, like many mp3 files. If you need moving more strange filenames check bellow.
- the above command is for dry run (only shows the mv commands with args). For real work remove the echo pretending mv .
ANY filename renaming. In the shell you need a delimiter. The problem is, than the filename (stored in a shell variable) usually can contain the delimiter itself, so:
mv $file $newfile #will fail, if the filename contains space, TAB or newline mv "$file" "$newfile" #will fail, if the any of the filenames contains "
the correct solution are either:
- prepare a filename with a proper escaping
- use a scripting language what easuly understands ANY filename
Preparing the correct escaping in bash is possible with it’s internal printf and %q formatting directive = print quoted . But this solution is long and boring.
IMHO, the easiest way is using perl and zero padded print0 , like next.
find . -name \*.jpg -print0 | perl -MFile::Basename -0nle 'rename $_, dirname($_)."/thumb_".basename($_)'
The above using perl’s power to mungle the filenames and finally renames the files.