Bash One Liner: copy template_*.txt to foo_*.txt? - bash

Say I have three files (template_*.txt):
template_x.txt
template_y.txt
template_z.txt
I want to copy them to three new files (foo_*.txt).
foo_x.txt
foo_y.txt
foo_z.txt
Is there some simple way to do that with one command, e.g.
cp --enableAwesomeness template_*.txt foo_*.txt

for f in template_*.txt; do cp $f foo_${f#template_}; done

[01:22 PM] matt#Lunchbox:~/tmp/ba$
ls
template_x.txt template_y.txt template_z.txt
[01:22 PM] matt#Lunchbox:~/tmp/ba$
for i in template_*.txt ; do mv $i foo${i:8}; done
[01:22 PM] matt#Lunchbox:~/tmp/ba$
ls
foo_x.txt foo_y.txt foo_z.txt

My preferred way:
for f in template_*.txt
do
cp $f ${f/template/foo}
done
The "I-don't-remember-the-substitution-syntax" way:
for i in x y z
do
cp template_$i foo_$
done

This should work:
for file in template_*.txt ; do cp $file `echo $file | sed 's/template_\(.*\)/foo_\1/'` ; done

for i in template_*.txt; do cp -v "$i" "`echo $i | sed 's%^template_%foo_%'`"; done
Probably breaks if your filenames have funky characters in them. Remove the '-v' when (if) you get confidence that it works reliably.

The command mmv (available in Debian or Fink or easy to compile yourself) was created precisely for this task. With the plain Bash solution, I always have to look up the documentation about variable expansion. But mmv is much simpler to use, quite close to "awesomeness"! ;-)
Your example would be:
mcp "template_*.txt" "foo_#1.txt"
mmv can handle more complex patterns as well and it has some sanity checks, for example, it will make sure none of the files in the destination set appear in the source set (so you can't accidentally overwrite files).

I don't know of anything in bash or on cp, but there are simple ways to do this sort of thing using (for example) a perl script:
($op = shift) || die "Usage: rename perlexpr [filenames]\n";
for (#ARGV) {
$was = $_;
eval $op;
die $# if $#;
rename($was,$_) unless $was eq $_;
}
Then:
rename s/template/foo/ *.txt

Yet another way to do it:
$ ls template_*.txt | sed -e 's/^template\(.*\)$/cp template\1 foo\1/' | ksh -sx
I've always been impressed with the ImageMagick convert program that does what you expect with image formats:
$ convert rose.jpg rose.png
It has a sister program that allows batch conversions:
$ mogrify -format png *.jpg
Obviously these are limited to image conversions, but they have interesting command line interfaces.

Related

Batch renaming files in MAC OSX

I have several thousand files with name like this:
PIN_PMN_PT_010_02_00331_0004_018edf
and need to rename them all something like this:
PIN_PMN_PT_010_02_00331_0004_018.edf
I have used simple mv scripts like this:
for f in *; do echo mv "$f" "`echo $f | tr 'edf' '.edf'`"; done
For some reason it creates names like this:
PIN_PMN_PT_010_02_00331_0004_018.ed
They are missing the last f. I am running the script using echo to dry run. Any ideas please?
Using MACBook Pro running Mohave 10.14.6 and Bash.
Use shell Parameter Expansion!
Simply under any shell:
For the test, echo to show what's will go:
for file in *edf ;do echo mv "$file" "${file%edf}.edf" ;done
Then, for doing the job:
for file in *edf ;do mv "$file" "${file%edf}.edf" ;done
(This must work same on MacOs, than under Linux.)
... And to prevent renaming of already correctly named files:
for file in *edf ;do test -f "${file##*.edf}" && mv "$file" "${file%edf}.edf" ;done
Syntax ${file##*.edf} will replace any string, terminated by .edf, by an empty string. So test -f "" will fail.
... Still: I don't have any Mac for doing the test, but as this is POSIX Standard, this must work on any shell. (Let my know, please comment!)
More infos?
Have a look at man sh or man bash and search for Parameter Expansion
man -P"less +'/Parameter Expansion'" bash
Using sed, this should work
for f in *; do echo mv "$f" "`echo $f | sed 's/.\{3\}$//`.edf"; done
You are just removing the last 3 characters of a string, and adding your file extension.
Personally, I find the rename command invaluable for this sort of thing:
rename 's/edf$/.edf/' *edf
If you want to do a dry-run, you can do:
rename --dry-run 's/edf$/.edf/' *edf
Sample Output
'PIN_PMN_PT_010_02_00331_0004_018edf' would be renamed to 'PIN_PMN_PT_010_02_00331_0004_018.edf'
The benefits of using rename are:
it can do a dry-run to test before you run for real
it will create all necessary directories with the -p option
it will not clobber (overwrite) files without warning
you have the full power of Perl available to you and can make your renaming as sophisticated as you wish.
As helpfully suggested by F. Hauri in the comments, you may have some files that have already had the dot inserted before the extension in your directory. To protect against insertion of a second dot, you could either be more specific in the files you select for renaming and only rename those ending in a digit followed by edf:
rename 's/edf$/.edf/' *[0-9]edf
Or, as F.Hauri suggested:
rename 's/([^.])edf$/$1.edf/' *edf
Note that you can install on macOS with homebrew:
brew install rename

Multifile rename Bash script issue

I was using a code to rename files and a very interesting problem emerged: It worked in a computer running Mac OSX Lion 10.7.5, but it failed on Mac OSX 10.6.8 Snow Leopard.
The code is:
for i in *; do mv $i `echo $i | sed 's/..//'`; done
and the error I got is:
usage: mv [-f | -i | -n] [-v] source target
mv [-f | -i | -n] [-v] source ... directory
It's very weird because mv is working normally if not in a for loop...
Anyone know what should I do to get it working?
You should use bash string substitution:
for i in *; do mv $i ${i/??/}; done
or
for i in *; do mv $i ${i#??}; done
If you have files with spaces (or certain other shell metachaarcters) it's critical that you put all variable references in double-quotes. Otherwise, if you have e.g. i="File Name.txt", you'll be running a command like mv File Name.txt le Name.txt and it'll think you're specifying 4 filenames not just 2. The other standard mistake (which you're not making) is using for file in $(ls), which gets confused by spaces before the names even make it into the variable.
Also, I'm a bit concerned about short filenames and name conflicts. Even if you think you don't have any short filenames, if you have the dotglob shell option set you'll wind up trying to rename the pseudo-files "." and "..", which won't go well at all. Also, if you have e.g. files named "abcdefg" and "cdefg", the script will rename the first over the second (silently erasing the second), then rename that to "efg".
So, here's my proposed rewrite (also using #TrueY's suggestion for shortening the filename):
for i in *; do
if [ ${#i} -le 2 ]; then
echo "$i: not renamed (too short)" >&2
elif [ -e "${i:2}" ]; then
echo "$i: not renamed (${i:2} already exists)" >&2
else
mv "$i" "${i:2}"
fi
done

Deleting files by date in a shell script?

I have a directory with lots of files. I want to keep only the 6 newest. I guess I can look at their creation date and run rm on all those that are too old, but is the a better way for doing this? Maybe some linux command I could use?
Thanks!
:)
rm -v $(ls -t mysvc-*.log | tail -n +7)
ls -t, list sorted by time
tail -n +7, +7 here means length-7, so all but first 7 lines
$() makes a list of strings from the enclosed command output
rm to remove the files, of course
Beware files with space in their names, $() splits on any white-space!
Here's my take on it, as a script. It does handle spaces in file names even if it is a bit of a hack.
#!/bin/bash
eval set -- $(ls -t1 | sed -e 's/.*/"&"/')
if [[ $# -gt 6 ]] ; then
shift 6
while [[ $# -gt 0 ]] ; do
echo "remove this file: $1" # rm "$1"
shift
done
fi
The second option to ls up there is a "one" for one file name per line. Doesn't actually seem to matter, though, since that appears to be the default when ls isn't feeding a tty.

grep spacing error

Hi guys i've a problem with grep . I don't know if there is another search code in shell script.
I'm trying to backup a folder AhmetsFiles which is stored in my Flash Disk , but at the same time I've to group them by their extensions and save them into [extensionName] Folder.
AhmetsFiles
An example : /media/FlashDisk/AhmetsFiles/lecture.pdf must be stored in /home/$(whoami)/Desktop/backups/pdf
Problem is i cant copy a file which name contains spaces.(lecture 2.pptx)
After this introduction here my code.
filename="/media/FlashDisk/extensions"
count=0
exec 3<&0
exec 0< $filename
mkdir "/home/$(whoami)/Desktop/backups"
while read extension
do
cd "/home/$(whoami)/Desktop/backups"
rm -rf "$extension"
mkdir "$extension"
cd "/media/FlashDisk/AhmetsFiles"
files=( `ls | grep -i "$extension"` )
fCount=( `ls | grep -c -i "$extension"` )
for (( i=0 ; $i<$fCount ; i++ ))
do
cp -f "/media/FlashDisk/AhmetsFiles/${files[$i]}" "/home/$(whoami)/Desktop/backups/$extension"
done
let count++
done
exec 0<&3
exit 0
Your looping is way more complicated than it needs to be, no need for either ls or grep or the files and fCount variables:
for file in *.$extension
do
cp -f "/media/FlashDisk/AhmetsFiles/$file" "$HOME/Desktop/backups/$extension"
done
This works correctly with spaces.
I'm assuming that you actually wanted to interpret $extension as a file extension, not some random string in the middle of the filename like your original code does.
Why don't you
grep -i "$extension" | while IFS=: read x ; do
cp ..
done
instead?
Also, I believe you may prefer something like grep -i ".$extension$" instead (anchor it to the end of line).
On the other hand, the most optimal way is probably
cp -f /media/FlashDisk/AhmetsFiles/*.$extension "$HOME/Desktop/backups/$extension/"

Using bash what scripts or commands do you use to make you more productive?

What scripts do you regularly use to improve your productivity?
Over the last year I have been trying to use bash scripts and commands to improve my productivity as a developer (Web and applications). Here is a list of a few simple ones that I use:
Make files lower case:
for i in *.txt; do mv "$i" "`echo $i| tr [A-Z] [a-z]`"; done
Test whether a tag exists in subversion:
if [ "`svn ls http://www.mysvnserver.co.uk/myproject/tags | grep it-0.7.0.1/`" = "it-0.7.0.1/" ]; then echo YES; else echo NO; fi
Rename all JPG files in the current directory and add an increment:
j=16;for i in *.jpg; do mv "$i" "gallery_"$j".jpg"; j=$(($j+1)); done;ls
Fix a misspelling in a group of filenames:
for i in aples*.jpg; do mv $i ${i/aples/apples} ; done
For more see here:
http://blog.emson.co.uk/2009/06/18-useful-bash-scripts-for-web-developers
What scripts do you use?
Thanks...
You have a good source of such command at CommandLineFu.

Resources