why are you using ls with an extra process to while loop? Just use a for loop with shell expansion. This is preferred way
#!/bin/bash
shopt -s nullglob
for file in *
do
if [ -f "$file" ];then
newfile="${file##* }"
mv "$file" $newfile"
fi
done
Answer from ghostdog74 on Stack Overflowwhy are you using ls with an extra process to while loop? Just use a for loop with shell expansion. This is preferred way
#!/bin/bash
shopt -s nullglob
for file in *
do
if [ -f "$file" ];then
newfile="${file##* }"
mv "$file" $newfile"
fi
done
Postman almost has it. For rename.sh, the awk command returns nothing so in effect you have the following command the shell attempts to execute:
mv rename.sh
hence the error message "missing destination file"
you can fix this by testing for the filename of the script, either hardcoded or $0, and executing the mv command only if $FILE does equal the script name.
#!/bin/bash
cd $1
for f in *; do
mv "$f" "${f%.*}.bu"
done
Try that instead (assuming that $1 is a folder):
for f in
f" "$f.bu"
done
Or use the rename utility:
rename -v 's/$/\.bu/' $1/*
Those commands add an extension. If you want to change the existing extension use that:
rename -v 's/\.[^\.]+$/\.bu/' $1/*
Copy and rename file from shell script
bash - Rename all files in directory from $filename_h to $filename_half? - Stack Overflow
shell script - rename files with rename command - Unix & Linux Stack Exchange
linux - How to rename multiple files in single command or script in Unix? - Unix & Linux Stack Exchange
Videos
You were right to consider rename first. The syntax is a little strange if you're not used to regexes but it's by far the quickest/shortest route once you know what you're doing:
rename 's/\d{4}/2503/' file*
That simply matches the first 4 numbers and swaps them for the ones you specified.
And a test harness (-vn means be verbose but don't do anything) using your filenames:
$ rename 's/\d{4}/2503/' file* -vn
file0901201437404.p renamed as file2503201437404.p
file0901201438761.p renamed as file2503201438761.p
file1003201410069.p renamed as file2503201410069.p
file2602201409853.p renamed as file2503201409853.p
file2602201410180.p renamed as file2503201410180.p
This should do the trick:
for f in file*; do mv
{f/${f:4:8}/25032014}; done
It replaces the string beween the 4th and the 12th character with "25032014".
This is a pretty basic question but I’ve been struggling getting this working for some reason. I am trying to copy a file from one directory to another and renaming it along with the copy. This is being done inside of a shell script, and I have a variable called $filename that stores the NEW file name. Here is the code snippet:
filename="IRcam_fpga_cksm_${checksum}_ver_${version}.pdb"
#filename="This_is_a_file.txt"
echo "filename: ${filename}"
cp ./par/ircam_fpga/designer/impl1/*.pdb output/pl/$filenameThe output of the echo command on the console is:
.pdbname: IRcam_fpga_cksm_A415_ver_0x0081
But the file that gets copied to the new directory does not have the correct name. When I use the version of $filename that is commented out, it works perfectly fine.
If you want to process files only in the top level of the directories (ie: one level deep), then you don't need recursion.
Both of these examples take a list of directories as arguments and rename only the files in those directories which match the more exclusive pattern: *-*.png (to avoid possible failures of the mv command).
A script that does NOT change directory:
In this first script, inside the for loop, the variable name contains the directory path to the file in addition to the base filename.
#!/bin/sh
for dir in "$@"
do
for name in "
name" "${name%-*}.png"
done
done
A script that DOES change directory:
In this second script, inside the for loop, the name variable contains only the filename, because the current directory has been temporarily changed to the script argument.
The code between the parenthesis ( and ), is executed in a subshell environment which means that changing the current directory, as well as variables set, will not be visible to the outer shell script, eliminating the need to change "back" to the original directory.
#!/bin/sh
for dir in "
dir"
then
for name in ./*-*.png
do
mv "$name" "${name%-*}.png"
done
fi
)
done
Notes About These Two Scripts:
The "$@" expands to the script's command line arguments. The script will silently do nothing with no arguments.
The directory-name arguments can be any absolute path (/path/to/dirx) or relative path (dirx, path/to/diry, ., .., ../x/d1, etc).
Messages will be printed on the standard error stream (stderr) by either mv or cd if a given directory does not exist, or if there are no *-*.png files in a directory. The script will continue processing subsequent directories.
The first line of these scripts can be #!/bin/sh instead of bash because this script uses none of the bash features, thus the more portable, posix compliant, and probably faster, sh can be used. Few scripts require the extra features of the bash shell. For these and other reasons #!/bin/bash is not generally recommended for scripting.
This worked for me
for dir in *; do if cd $dir; then for filename in *; do mv $filename "$filename.pdf"; done; cd ..; fi; done
Just use bash, no need to call external commands.
for file in *_h.png
do
mv "$file" "${file/_h.png/_half.png}"
done
Do not add #!/bin/sh
For those that need that one-liner:
for file in *.png; do mv "$file" "${file/_h.png/_half.png}"; done
Try rename command:
rename 's/_h.png/_half.png/' *.png
Update:
example usage:
create some content
$ mkdir /tmp/foo
touch one_h.png two_h.png three_h.png
$ ls
one_h.png three_h.png two_h.png
test solution:
$ rename 's/_h.png/_half.png/' *.png
$ ls
one_half.png three_half.png two_half.png
Temporary note: there is something wrong - the rename pattern does not handle filenames with path; I'm working on a fix
What is wrong in your command is two things:
find /tmp/dir -name "* *" -type f | rename 's/*/fixed_/g'
The
-name "* *"matches only file names with a space in it - that's not what you want, right?- the solution is to just leave it out; We do not want to exclude files matching some name - we want all.
The
renamepattern is wrong in two ways- you used a shell glob pattern, but it needs to be a regular expression, in short a regex (It can be some general perl expression - let's ignore it and use only
s///g) - the fixed pattern would match the complete name, and replace it with
fixed_. You want to "replace" the "first 0 characters" withfixed_, technically. That's the start of the line, matched with^. We can leave out thegbecause there is only one replacement needed per line.
- you used a shell glob pattern, but it needs to be a regular expression, in short a regex (It can be some general perl expression - let's ignore it and use only
Putting it together, it looks like this:
find /tmp/dir -type f | rename 's/^/fixed_/'
It seems your system features the older version of rename that doesn't use regular expressions. You can rename your files with
cd /tmp/dir/
for f in *\ * ; do
[[ -f $f ]] && mv "$f" fixed_"$f"
done
(untested)
For the rename command with regex support, you have to change the regular expression: * needs something to operate on, it means "repeat the previous thing zero or more times". You don't want to replace anything, you want to prepend, so use ^ which stands for the beginning of the string:
rename 's/^/fixed_/'
Most standard shells provide a way to do simple text substitution within shell variables. http://tldp.org/LDP/abs/html/parameter-substitution.html explains as follows:
${var/Pattern/Replacement}
First match of Pattern, within var replaced with Replacement.
So use this script to loop through all the appropriate files and rename each of them:
for file in aro_tty-mIF-*_opt
do
mv -i "${file}" "${file/-mIF-/-mImpFRA-}"
done
I have added a -i option so you have the chance to confirm each renaming operation. As always, you should make a backup of all your files before doing any large amount of renaming or deleting.
If you don't have Perl's rename:
perl -e '
FILE:for $file (@ARGV){
($new_name = $file) =~ s/-mIF-/-mImpFRA-/
next FILE if -e $new_name;
rename $file => $new_name
}' *_opt
If you do have Perl's rename:
rename 's/-mIF-/-mImpFRA-/' *_opt
Escape the space, e.g. Spring\ 2011, or use quotes, e.g. 'Spring 2011'. In the future, it's typically a bad idea to use file names with spaces in them on any *NIX.
If you've got rename, you can use this:
rename ' ' '_' [filenames...]
If you don't have rename or prefer to use just the shell:
for f in *\ *; do mv "$f" "${f// /_}"; done
Broken down:
*\ *selects all files with a space in their name as input for the theforloop. The pattern*X*selects all files withXin their name, and for the special character space, we have to escape it with a slash so that bash doesn't treat it as separating different arguments.- The quotes around
"$f"are important because we know there's a space in the filename and otherwise it would appear as 2+ arguments tomv. ${f//str/new_str}is a bash-specific string substitution feature. All instances ofstrare replaced withnew_str.
To do this in a single command, you can simply do this:
mv some/long/path/to/file/{myfiel.txt,myfile.txt}
Which is an example for the full file name, given that it's a typo you can do something like:
mv some/long/path/to/file/myfi{el,le}.txt
Both will expand to the full command, these are called brace expansions. They are supported by zsh.
Here are several options:
Change to the directory:
cd /home/long/path
mv file1 file2
cd -
Change directories using the directory stack:
pushd /some/long/path
mv file1 file2
popd
Change to the directory using a subshell:
(
cd /some/long/path
mv file1 file2
) # no need to change back
Use brace expansion:
mv /some/long/path/{file1,file2}
Use a variable:
D=/some/long/path
mv "$D/file1" "$D/file2"