|
Home > Archive > Unix Shell > January 2006 > modifying case of dirs and files name
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
modifying case of dirs and files name
|
|
| elerdin 2006-01-22, 6:10 pm |
| Hallo, I'm trying to recursively change the case of names of files and
subdirs into a directory using bash, I created some nested directrories
with some
files:
$ find . -depth
../prova.sh
../pollo/pippo/prova2
../pollo/pippo/pluto
../pollo/pippo
../pollo/prova1
../pollo/prova con spazi
../pollo
..
and this script:
###
#! /bin/bash
lista=( $(find . -depth | sed "s/ /_/g" | sed "s/^\.//g" | sed
"s/^\///g") )
for i in $(seq 0 $((${#lista[@]} - 1)))
do
percorso=`echo '"'`${lista[$i]}`echo '"'` # for managing spaces
dir=`dirname $percorso`
base=`basename $percorso`
mv `echo $percorso | sed "s/_/ /g"` `echo $dir | sed "s/_/
/g"``echo "/"``echo $base | sed "s/_/ /g" | tr [:lower:] [:upper:] |
sed "s/_/ /g"`
done
###
but it returns:
mv: cannot stat `"prova.sh"': No such file or directory
mv: cannot stat `"pollo/pippo/prova2"': No such file or directory
mv: cannot stat `"pollo/pippo/pluto"': No such file or directory
mv: cannot stat `"pollo/pippo"': No such file or directory
mv: cannot stat `"pollo/prova1"': No such file or directory
mv: when moving multiple files, last argument must be a directory
Try `mv --help' for more information.
mv: cannot stat `"pollo"': No such file or directory
If I echo the command-line created by the script and it seems to be ok
and if I copy and paste it on the shell it works, so I suppose the
problem is how bash manage something into the command I create.
Has someone any suggestion?
Thanks, Elerdin.
| |
| Dave Kelly 2006-01-22, 6:10 pm |
| elerdin wrote:
> Hallo, I'm trying to recursively change the case of names of files and
> subdirs into a directory using bash, I created some nested directrories
> with some
> files:
>
>te it on the shell it works, so I suppose the
> problem is how bash manage something into the command I create.
>
> Has someone any suggestion?
>
> Thanks, Elerdin.
>
Unless you need to reinvent the wheel for a homework assignment or
something, investigate the power of 'tr'. There is a manual and/or find
a copy of 'Linux in a Nutshell' online and peruse their examples.
| |
| Ed Morton 2006-01-22, 6:10 pm |
| elerdin wrote:
> Hallo, I'm trying to recursively change the case of names of files and
> subdirs into a directory using bash, I created some nested directrories
> with some
> files:
>
> $ find . -depth
> ./prova.sh
> ./pollo/pippo/prova2
> ./pollo/pippo/pluto
> ./pollo/pippo
> ./pollo/prova1
> ./pollo/prova con spazi
> ./pollo
> .
>
> and this script:
>
> ###
> #! /bin/bash
>
> lista=( $(find . -depth | sed "s/ /_/g" | sed "s/^\.//g" | sed
> "s/^\///g") )
>
> for i in $(seq 0 $((${#lista[@]} - 1)))
> do
> percorso=`echo '"'`${lista[$i]}`echo '"'` # for managing spaces
>
> dir=`dirname $percorso`
> base=`basename $percorso`
>
> mv `echo $percorso | sed "s/_/ /g"` `echo $dir | sed "s/_/
> /g"``echo "/"``echo $base | sed "s/_/ /g" | tr [:lower:] [:upper:] |
> sed "s/_/ /g"`
>
> done
>
> ###
Well, you certainly did attempt it first! It's not 100% clear what the
above is attempting to do or what your requirements are, so try this first:
find . -print | while IFS= read -r dirfile
do
srcDir="${dirfile%/*}"
srcFile="${dirfile##*/}"
destDir=`echo "$srcDir" | tr '[a-z]' '[A-Z]'`
destFile=`echo "$srcFile" | tr '[a-z]' '[A-Z]'`
echo "a) ${srcDir}/${srcFile} -> ${destDir}/${destFile}"
echo "b) ${srcDir}/${srcFile} -> ${srcDir}/${destFile}"
done
and tell us which of the transformations, a or b, if any, is what you
actually want. If neither, what do you want? It would also be useful to
know which shell you're using.
Ed.
| |
| Michael Paoli 2006-01-22, 6:10 pm |
| elerdin wrote:
> Hallo, I'm trying to recursively change the case of names of files and
> subdirs into a directory using bash, I created some nested directrories
>
> #! /bin/bash
> lista=( $(find . -depth | sed "s/ /_/g" | sed "s/^\.//g" | sed
> "s/^\///g") )
> for i in $(seq 0 $((${#lista[@]} - 1)))
> do
> percorso=`echo '"'`${lista[$i]}`echo '"'` # for managing spaces
> dir=`dirname $percorso`
> base=`basename $percorso`
> mv `echo $percorso | sed "s/_/ /g"` `echo $dir | sed "s/_/
> /g"``echo "/"``echo $base | sed "s/_/ /g" | tr [:lower:] [:upper:] |
> sed "s/_/ /g"`
> done
>
> mv: cannot stat `"prova.sh"': No such file or directory
> mv: when moving multiple files, last argument must be a directory
>
> If I echo the command-line created by the script and it seems to be ok
> and if I copy and paste it on the shell it works, so I suppose the
> problem is how bash manage something into the command I create.
Study the Frequently Asked Questions (FAQ), most notably 2.6):
Message-ID: <unix-faq/faq/part2_1136785841@rtfm.mit.edu>
http://groups.google.com/group/comp...c76fcc5a70422db
(Re)read and study the bash(1) manual pages, most notably the sections
on echo, quoting, and command substitution.
references/excerpts:
Message-ID: <unix-faq/faq/part2_1136785841@rtfm.mit.edu>
http://groups.google.com/group/comp...c76fcc5a70422db
bash(1) *
test(1) *
[(1) *
expr(1) *
echo(1) *
ksh(1) *
sh(1) *
tr(1)
sed(1)
perl(1)
* note that in many cases, many of these utilities also exist as
built-ins for many of the more modern shells and implementations
thereof.
| |
| elerdin 2006-01-22, 6:10 pm |
| Thanks, to all for your fast answers, but all solutions you're
suggesting cover simplier case only. I'm using bash. The script is for
changing the case of both names of files and names of directory
recursively and must manage spaces in names.
I hope I can explain better, I have this directory structure:
../DIR1/DIR2/DIR3/FILE3.TXT
../DIR1/DIR2/DIR3
../DIR1/DIR2/FILE2.TXT
../DIR1/DIR2
../DIR1/FILE WITH SPACES.TXT
../DIR1/FILE1.TXT
../DIR1
I want to obtain this:
../dir1/dir2/dir3/file3.txt
../dir1/dir2/dir3
../dir1/dir2/file2.txt
../dir1/dir2
../dir1/file with spaces.txt
../dir1/file1.txt
../dir1
I create that script, but I do not understand why it does not work. I
post another time the script:
###
#! /bin/bash
list=( $(find . -depth | sed "s/ /_/g") )
for i in $(seq 0 $((${#list[@]} - 1)))
do
fullpath=`echo '"'`${list[$i]}`echo '"'`
dir=`dirname $fullpath`
base=`basename $fullpath`
echo "mv "`echo $fullpath | sed "s/_/ /g"`" "`echo $dir | sed "s/_/
/g"``echo "/"``echo $base | sed "s/_/ /$done
###
As you can see the "mv" line, the last one, is echoed, the command it
output are all correct, and if I put the into a file and execute that
file all works fine, but I do not understand why the same commands do
not work INSIDE the script. The problem is the "mv" line for sure, but
I do not understan where, probably the bash does something I do not
understand.
Thanks, Elerdin.
| |
| Stachu 'Dozzie' K. 2006-01-22, 6:10 pm |
| On 22.01.2006, elerdin <elerdin@gmail.com> wrote:
> Thanks, to all for your fast answers, but all solutions you're
> suggesting cover simplier case only. I'm using bash. The script is for
> changing the case of both names of files and names of directory
> recursively and must manage spaces in names.
>
> I hope I can explain better, I have this directory structure:
>
> ./DIR1/DIR2/DIR3/FILE3.TXT
> ./DIR1/DIR2/DIR3
> ./DIR1/DIR2/FILE2.TXT
> ./DIR1/DIR2
> ./DIR1/FILE WITH SPACES.TXT
> ./DIR1/FILE1.TXT
> ./DIR1
>
> I want to obtain this:
>
> ./dir1/dir2/dir3/file3.txt
> ./dir1/dir2/dir3
> ./dir1/dir2/file2.txt
> ./dir1/dir2
> ./dir1/file with spaces.txt
> ./dir1/file1.txt
> ./dir1
>
> I create that script, but I do not understand why it does not work.
Actually, I don't understand why do you expect it to work correctly.
It's too complicated for such simple task.
> As you can see the "mv" line, the last one, is echoed, the command it
> output are all correct, and if I put the into a file and execute that
> file all works fine, but I do not understand why the same commands do
> not work INSIDE the script. The problem is the "mv" line for sure, but
> I do not understan where,
Nope. The problem is not in the line with mv. The problem is in your
whole script. It's one big mess. Let me explain:
> #! /bin/bash
>
> list=( $(find . -depth | sed "s/ /_/g") )
What would it do if name of some directory with files contains spaces?
Another thing, let's assume you don't have dirs with spaces. How would
you rename './ble/file with spaces.txt'? You would only get
'./ble/file_with_spaces.txt'.
> for i in $(seq 0 $((${#list[@]} - 1)))
> do
What is that `seq` for? Do you really need the index of current array
element? Let me guess: you have some programming experience in C or
Pascal and nothing of higher level, right? In most scripting languages
(and many compiled ones) an array is a list and you can move through
whole list using element by element. This is the correct way to process
each element in bash:
#v+
for element in "${list[@]}"; do
#v-
> fullpath=`echo '"'`${list[$i]}`echo '"'`
What are all these quote characters for? Explain it[*]. And then,
rewrite this assignment.
> dir=`dirname $fullpath`
> base=`basename $fullpath`
If $fullpath contains in some magical way spaces, then how would dirname
react? And basename? Another matter is the $fullpath will never contain
spaces, but names of your files will do.
> echo "mv "`echo $fullpath | sed "s/_/ /g"`" "`echo $dir | sed "s/_/
> /g"``echo "/"``echo $base | sed "s/_/ /$done
Ouch. Too many `echo|sed's and misused quote characters for me.
Again, try to explain every single quote character[*].
Last of all, I'll use "find ... | PERL -e '...'", since PERL is able to
manipulate filenames and then rename files without exec()ing lots of
processes, which would speed up the command significantly for large
number of files (>500). And yes, I did use `find|perl' this way.
[*] I really mean "explain". What it should it do? And what it is
actually doing? I know from experience that this helps very much in
understanding explained thing.
--
Feel free to correct my English
Stanislaw Klekot
| |
| Ed Morton 2006-01-22, 8:58 pm |
| elerdin wrote:
> Thanks, to all for your fast answers, but all solutions you're
> suggesting cover simplier case only. I'm using bash. The script is for
> changing the case of both names of files and names of directory
> recursively and must manage spaces in names.
>
> I hope I can explain better, I have this directory structure:
>
> ./DIR1/DIR2/DIR3/FILE3.TXT
> ./DIR1/DIR2/DIR3
> ./DIR1/DIR2/FILE2.TXT
> ./DIR1/DIR2
> ./DIR1/FILE WITH SPACES.TXT
> ./DIR1/FILE1.TXT
> ./DIR1
>
> I want to obtain this:
>
> ./dir1/dir2/dir3/file3.txt
> ./dir1/dir2/dir3
> ./dir1/dir2/file2.txt
> ./dir1/dir2
> ./dir1/file with spaces.txt
> ./dir1/file1.txt
> ./dir1
That's just this:
find . -print | while IFS= read -r src
do
dest=`echo "$src" | tr '[A-Z]' '[a-z]'`
srcDir="${src%/*}"
destDir="${dest%/*}"
mkdir -p "$destDir"
mv "$src" "$dest"
done
Regards,
Ed.
| |
| Michael Paoli 2006-01-23, 2:55 am |
| elerdin wrote:
> Thanks, to all for your fast answers, but all solutions you're
> suggesting cover simplier case only. I'm using bash. The script is for
> changing the case of both names of files and names of directory
> recursively and must manage spaces in names.
Maybe you didn't have a chance to see what I'd posted earlier. Didn't
exactly spell out a full answer, but provided hints and pointers to the
essential information resources:
Message-ID: <1137952831.100858.131330@g44g2000cwa.googlegroups.com>
http://groups.google.com/group/comp...7909944d4615bdb
I suggest studying that stuff (totally unbiased opinion, of course).
;-)
> I hope I can explain better, I have this directory structure:
> ./DIR1/DIR2/DIR3/FILE3.TXT
> ./DIR1/DIR2/DIR3
> ./DIR1/DIR2/FILE2.TXT
> ./DIR1/DIR2
> ./DIR1/FILE WITH SPACES.TXT
> ./DIR1/FILE1.TXT
> ./DIR1
> I want to obtain this:
> ./dir1/dir2/dir3/file3.txt
> ./dir1/dir2/dir3
> ./dir1/dir2/file2.txt
> ./dir1/dir2
> ./dir1/file with spaces.txt
> ./dir1/file1.txt
> ./dir1
> I create that script, but I do not understand why it does not work. I
> post another time the script:
> #! /bin/bash
> list=( $(find . -depth | sed "s/ /_/g") )
> for i in $(seq 0 $((${#list[@]} - 1)))
> do
> fullpath=`echo '"'`${list[$i]}`echo '"'`
> dir=`dirname $fullpath`
> base=`basename $fullpath`
> echo "mv "`echo $fullpath | sed "s/_/ /g"`" "`echo $dir | sed "s/_/
> /g"``echo "/"``echo $base | sed "s/_/ /$done
> As you can see the "mv" line, the last one, is echoed, the command it
> output are all correct, and if I put the into a file and execute that
> file all works fine, but I do not understand why the same commands do
> not work INSIDE the script. The problem is the "mv" line for sure, but
> I do not understan where, probably the bash does something I do not
> understand.
Okay, ... how about some more hints and a potentially useful example:
$ find DIR1 -print
DIR1
DIR1/DIR2
DIR1/DIR2/DIR3
DIR1/DIR2/DIR3/FILE3.TXT
DIR1/DIR2/FILE2.TXT
DIR1/FILE1.TXT
DIR1/FILE WITH SPACES.TXT
$ ./lcase
$ find dir1 -print
dir1
dir1/dir2
dir1/dir2/dir3
dir1/dir2/dir3/file3.txt
dir1/dir2/file2.txt
dir1/file with spaces.txt
dir1/file1.txt
$ cat lcase
#!/bin/sh
find . -depth -name '*[A-Z]*' -exec bash -c '
t="`echo -nE '''{}''' | sed -e '''s![^/]*$!\L&!'''`"
if [ -a "$t" ] || [ -h "$t" ]; then
1>&2 echo "$0: $t already exists"
else
mv -f '''{}''' "$t"
fi
' \;
$
If one carefully studies bash(1), particularly all the quoting and
command substitution stuff and also the echo and test ([) built-in
to bash(1), and also find(1), sed(1) and mv(1), the above example
should all start to make lots of sense (as should why your earlier
example didn't achieve what you wanted).
And extra credit: The example I wrote won't work perfectly in all
cases. Find one or more flaws or potential unexpected behaviors (e.g.
where it won't precisely and only change the case of the the
filename/directory).
And at least some possible extra credit answers/hints (rot13(6)):
genvyvat arjyvar(f)
anzr pbyyvfvba grfgvat naq genvyvat arjyvar(f)
rkvg inyhr beqre qrcraqrapl
| |
| Stephane CHAZELAS 2006-01-23, 2:55 am |
| 2006-01-22, 11:57(-08), elerdin:
> Thanks, to all for your fast answers, but all solutions you're
> suggesting cover simplier case only. I'm using bash. The script is for
> changing the case of both names of files and names of directory
> recursively and must manage spaces in names.
>
> I hope I can explain better, I have this directory structure:
>
> ./DIR1/DIR2/DIR3/FILE3.TXT
> ./DIR1/DIR2/DIR3
> ./DIR1/DIR2/FILE2.TXT
> ./DIR1/DIR2
> ./DIR1/FILE WITH SPACES.TXT
> ./DIR1/FILE1.TXT
> ./DIR1
>
> I want to obtain this:
>
> ./dir1/dir2/dir3/file3.txt
> ./dir1/dir2/dir3
> ./dir1/dir2/file2.txt
> ./dir1/dir2
> ./dir1/file with spaces.txt
> ./dir1/file1.txt
> ./dir1
[...]
Use zsh:
autoload -U zmv # if not already in your ~/.zshrc
zmv '(**/)(*)' '$1${(L)2}'
--
Stéphane
| |
| Xicheng 2006-01-23, 6:13 pm |
| elerdin wrote:
> Thanks, to all for your fast answers, but all solutions you're
> suggesting cover simplier case only. I'm using bash. The script is for
> changing the case of both names of files and names of directory
> recursively and must manage spaces in names.
The problem could be easier if you handle files and directories
separately.
#changing files:
find -type f | PERL -nle 'm#[a-z]+[^/]*$# and m#^(.*/)(.*?)$# and print
"mv \"$_\" \"$1\U$2\""' | sh
1) use PERL to separate filename with dirname, and print out only
filenames containing at least one lower-case character. then print out
in the following form:
m#[a-z]+[^/]*$# ---> at least one [a-z] in filename
m#^(.*/)(.*?)$# --> separate filename with dirname
print "mv \"$_\" \"$1\U$2\""' --> printout mv "$oldname" "$newname"
use quotes to enclose two arguments of "mv", which can solve the
problem of spaces in the filename/dirname.
#changing directories
find -type d | tac | PERL -nle 'm#[a-z]+[^/]*$# and m#^(.*/)(.*?)$# and
print "mv \"$_\" \"$1\U$2\""' | sh
2) when operating on directories, use "tac" to reverse the output of
"find", and each time you change only the rightmost subdirectory. This
way you wouldnt get complained because you won't change the name of
parent-directory before that of sub-directory...
HTH,
Xicheng
> I hope I can explain better, I have this directory structure:
>
> ./DIR1/DIR2/DIR3/FILE3.TXT
> ./DIR1/DIR2/DIR3
> ./DIR1/DIR2/FILE2.TXT
> ./DIR1/DIR2
> ./DIR1/FILE WITH SPACES.TXT
> ./DIR1/FILE1.TXT
> ./DIR1
>
> I want to obtain this:
>
> ./dir1/dir2/dir3/file3.txt
> ./dir1/dir2/dir3
> ./dir1/dir2/file2.txt
> ./dir1/dir2
> ./dir1/file with spaces.txt
> ./dir1/file1.txt
> ./dir1
>
> I create that script, but I do not understand why it does not work. I
> post another time the script:
>
> ###
> #! /bin/bash
>
> list=( $(find . -depth | sed "s/ /_/g") )
>
> for i in $(seq 0 $((${#list[@]} - 1)))
> do
> fullpath=`echo '"'`${list[$i]}`echo '"'`
>
> dir=`dirname $fullpath`
> base=`basename $fullpath`
>
> echo "mv "`echo $fullpath | sed "s/_/ /g"`" "`echo $dir | sed "s/_/
> /g"``echo "/"``echo $base | sed "s/_/ /$done
>
> ###
>
> As you can see the "mv" line, the last one, is echoed, the command it
> output are all correct, and if I put the into a file and execute that
> file all works fine, but I do not understand why the same commands do
> not work INSIDE the script. The problem is the "mv" line for sure, but
> I do not understan where, probably the bash does something I do not
> understand.
>
> Thanks, Elerdin.
| |
| bob the builder 2006-01-23, 8:49 pm |
|
elerdin wrote:
> Hallo, I'm trying to recursively change the case of names of files and
> subdirs into a directory using bash, I created some nested directrories
> with some
> files:
>
snip
Iv'e been tryin g to work on name portability for a while, truth be
told, zsh aside, shell scripting isn't well suited for it, especially
if the files have embeded unicode and newlines, here is a Python script
i put together.
basically you save it as _portable_renamer.py and run it like such:
../_portable_renamer /path/to/jacked/up/files doit
any string other than "doit" as a third argument causes the whole thing
to act in a read only manner.
feel free to modify, sorry if you shell zealots think this is spam.
#!/usr/bin/env python
import os
import sys
import string
import platform
dir = sys.argv[1]
noworky = sys.argv[2]
if platform.system() == 'Linux':
uglychars = ''.join( set(string.punctuation+' ') - set('/_.') )
else:
if platform.system() == 'Windows':#this is broken because windows
is gay with case
uglychars = ''.join( set(string.punctuation+' ') -
set(':\\/_.') )
else:
print "wtf... what platform is this anyway?"
underscore = '_'
underscore = underscore * len(uglychars)
chars = string.maketrans(uglychars, underscore)
print "# PHASE I, DIRECTORIES"
for path, subdirs, files in os.walk(dir, topdown=True):
oldname = path
newname = oldname.translate(chars)
newname = string.lower(newname)
while string.count(newname, "__") > 0:
newname = string.replace(newname,"__","_")
while string.count(newname, "..") > 0:
newname = string.replace(newname,"..",".")
if oldname != newname:
if os.path.isfile(newname) or os.path.isdir(newname):
print oldname, "-->\n", newname, "\t\t\tERROR: file/dir
exists\n"
else:
print oldname, "-->\n", newname, "\t\t\tYAY: file
renamed\n"
if noworky == "doit":
os.renames(oldname, newname)
print "# PHASE II, FILES"
for path, subdirs, files in os.walk(dir, topdown=True):
for oldname in files:
oldname = os.path.join(path, oldname)
newname = oldname.translate(chars)
newname = string.lower(newname)
newname = string.replace(newname,".mpeg",".mpg")
newname = string.replace(newname,".ram",".rm")
newname = string.replace(newname,".jpeg",".jpg")
newname = string.replace(newname,".qt",".mov")
while string.count(newname, "__") > 0:
newname = string.replace(newname,"__","_")
while string.count(newname, "..") > 0:
newname = string.replace(newname,"..",".")
newname = string.replace(newname,"._","_")
newname = string.replace(newname,"_.",".")
if oldname != newname:
if os.path.isfile(newname) or os.path.isdir(newname):
print oldname, "-->\n", newname, "\t\t\tERROR: file/dir
exists\n"
else:
print oldname, "-->\n", newname, "\t\t\tYAY: file
renamed\n"
if noworky == "doit":
os.renames(oldname, newname)
| |
| Chris F.A. Johnson 2006-01-23, 8:49 pm |
| On 2006-01-24, bob the builder wrote:
>
> elerdin wrote:
> snip
>
> Iv'e been tryin g to work on name portability for a while, truth be
> told, zsh aside, shell scripting isn't well suited for it,
I've seen many shell scripts which do it portably, and many which
take advantage of specific extensions, such as typeset -u in ksh,
or, in bash, William Park's extensions, or my loadable builtins,
ucase and lcase.
> especially if the files have embeded unicode and newlines,
With a litle care, a shell script can handle any characters in
filenames.
--
Chris F.A. Johnson, author | <http://cfaj.freeshell.org>
Shell Scripting Recipes: | My code in this post, if any,
A Problem-Solution Approach | is released under the
2005, Apress | GNU General Public Licence
| |
| bob the builder 2006-01-23, 8:49 pm |
|
Chris F.A. Johnson wrote:
> On 2006-01-24, bob the builder wrote:
>
> I've seen many shell scripts which do it portably, and many which
> take advantage of specific extensions, such as typeset -u in ksh,
> or, in bash, William Park's extensions, or my loadable builtins,
> ucase and lcase.
>
>
> With a litle care, a shell script can handle any characters in
> filenames.
well I really love bash, and I've seen your solution... its effective.
and long.
anything this simple should not take that many lines of code... if it
does, then the language is ill suited for the job, imho.
and zmv is a beautiful thing to watch, but it can only really do one
thing at a time, so that means slow....
I am sure the experts will refute these claims though.
I am of the humble opinion that the terser the code the better, as the
probability of a bug sneaking in undetected increases exponentially
with the number of lines involved.
| |
| Stachu 'Dozzie' K. 2006-01-24, 2:49 am |
| On 24.01.2006, bob the builder <ewaguespack@gmail.com> wrote:
>
> Chris F.A. Johnson wrote:
>
>
> well I really love bash, and I've seen your solution... its effective.
>
> and long.
I didn't. Where did Chris posted his code in this thread?
> anything this simple should not take that many lines of code... if it
> does, then the language is ill suited for the job, imho.
Shell is well suited. Especially shell aided by some other tools, such
as GNU find and Perl.
#v+
find . -depth -print0 | PERL -0lne '
BEGIN{$\ = "\n"; $, = " -> "}
$old = $_; s|(/[^/]*$)|lc $1|e;
print $old, $_ if $_ ne $old
'
#v-
When you replace "print" with "rename" and optionally remove BEGIN{}
block, then the code is ready to do massive renaming. Nobody said that
you can't use PERL or Ruby interpreter when it is appropriate, and shell
glues find and PERL in very convenient way.
--
Feel free to correct my English
Stanislaw Klekot
| |
| Xicheng 2006-01-24, 2:49 am |
| Stachu 'Dozzie' K. wrote:
> On 24.01.2006, bob the builder <ewaguespack@gmail.com> wrote:
> Shell is well suited. Especially shell aided by some other tools, such
> as GNU find and Perl.
>
> #v+
> find . -depth -print0 | PERL -0lne '
> BEGIN{$\ = "\n"; $, = " -> "}
> $old = $_; s|(/[^/]*$)|lc $1|e;
> print $old, $_ if $_ ne $old
> '
the -depth option is really wonderful, I didnt even find it on my man
page of 'find' command, but it's really powerful. :-) and make life
easier...
Good day,
Xicheng
> #v-
>
> When you replace "print" with "rename" and optionally remove BEGIN{}
> block, then the code is ready to do massive renaming. Nobody said that
> you can't use PERL or Ruby interpreter when it is appropriate, and shell
> glues find and PERL in very convenient way.
>
> --
> Feel free to correct my English
> Stanislaw Klekot
| |
| Stachu 'Dozzie' K. 2006-01-24, 2:49 am |
| On 24.01.2006, Xicheng <xicheng@gmail.com> wrote:
> Stachu 'Dozzie' K. wrote:
> the -depth option is really wonderful, I didnt even find it on my man
> page of 'find' command, but it's really powerful. :-) and make life
> easier...
Before I got know about -depth I used `sort -rz'. For any reasonable
number of files it's fast enough.
--
Feel free to correct my English
Stanislaw Klekot
| |
| Chris F.A. Johnson 2006-01-24, 2:49 am |
| On 2006-01-24, bob the builder wrote:
>
> I am of the humble opinion that the terser the code the better, as the
> probability of a bug sneaking in undetected increases exponentially
> with the number of lines involved.
I agree that code should be as short as possible. That does not
necessarily mean that it will be short. When you use a compiled
utility, you are probably executing more lines of code than it
would take to do the same thing with a shell function.
Even a short program in a language such as C will make use of
large quantities of code in the standard library.
For example, in my library of arithmetic shell functions, I have
one for multiplying decimal fractions. It is more than 80 lines
long. Do you think that writing:
.. math-funcs
fpmul 1.23 45.67
is using more code than:
awk 'BEGIN { print 1.23 45.67; exit }'
Take a look at the source code for awk and say that with a
straight face.
If I rewrote the fpmul code every time I needed to multiply two
decimal fractions, then yes, the probability of a bug would be
high. On the other hand, by using a function with well debugged
code, that is not an issue.
That is why I put a lot of emphasis in my book on reusable
function libraries.
--
Chris F.A. Johnson, author | <http://cfaj.freeshell.org>
Shell Scripting Recipes: | My code in this post, if any,
A Problem-Solution Approach | is released under the
2005, Apress | GNU General Public Licence
| |
| Stephane CHAZELAS 2006-01-24, 2:49 am |
| 2006-01-22, 08:28(-08), elerdin:
> Hallo, I'm trying to recursively change the case of names of files and
> subdirs into a directory using bash, I created some nested directrories
> with some
> files:
[...]
Portably (POSIXly):
find .//. -depth -print |
awk -F/ -vOFS=/ '
function escape(s) {
gsub(/'''/, "'''\\''''", s)
return "'''" s "'''"
}
function process_file(file) {
$0 = file
$NF = toupper($NF)
print "mv -i " escape(file) " " escape($0)
}
NR == 1 {
file = substr($0, 4)
next
}
/\/\// {
line = substr($0, 4)
process_file(file)
file = line
next
}
{
file = file "\n" $0
}
END {
if (file != "")
process_file(file)
}' | sh
--
Stéphane
| |
| Stephane CHAZELAS 2006-01-24, 2:49 am |
| 2006-01-23, 18:05(-08), bob the builder:
[...]
> anything this simple should not take that many lines of code... if it
> does, then the language is ill suited for the job, imho.
That's not this simple, especially if you want to resolve
conflicts (which zmv does).
> and zmv is a beautiful thing to watch, but it can only really do one
> thing at a time, so that means slow....
Not sure what you mean. If you mean that mv is called for each
file, then make mv builtin:
~$ type mv
mv is /bin/mv
~$ zmodload zsh/files
~$ type mv
mv is a shell builtin
If you mean that the list is first built, and then the files in
that list renamed, then zmv has to do it that way to resolve
potential conflicts.
You can also use mmv for that:
mmv ';*' '#1#l2'
But once you have zsh, you don't need mmv. zmv can do a lot more
than what mmv can has it has all the expansion operators of zsh.
(mmv can't do padding or arithmetics or use associative arrays
or call commands like "file" for instance).
--
Stéphane
| |
| William James 2006-01-24, 6:23 pm |
| elerdin wrote:
> Thanks, to all for your fast answers, but all solutions you're
> suggesting cover simplier case only. I'm using bash. The script is for
> changing the case of both names of files and names of directory
> recursively and must manage spaces in names.
>
> I hope I can explain better, I have this directory structure:
>
> ./DIR1/DIR2/DIR3/FILE3.TXT
> ./DIR1/DIR2/DIR3
> ./DIR1/DIR2/FILE2.TXT
> ./DIR1/DIR2
> ./DIR1/FILE WITH SPACES.TXT
> ./DIR1/FILE1.TXT
> ./DIR1
>
> I want to obtain this:
>
> ./dir1/dir2/dir3/file3.txt
> ./dir1/dir2/dir3
> ./dir1/dir2/file2.txt
> ./dir1/dir2
> ./dir1/file with spaces.txt
> ./dir1/file1.txt
> ./dir1
>
Using the Ruby language, it's a one-liner:
ruby -e 'Dir["**/*"].each{|x|File.rename(x,x.downcase)}'
| |
| William James 2006-01-24, 6:23 pm |
| William James wrote:
> elerdin wrote:
>
> Using the Ruby language, it's a one-liner:
>
> ruby -e 'Dir["**/*"].each{|x|File.rename(x,x.downcase)}'
Children need to be changed before the parent:
ruby -e 'Dir["**/*"].reverse.each{|x|File.rename(x,x.downcase)}'
| |
| Stephane CHAZELAS 2006-01-24, 6:23 pm |
| 2006-01-24, 02:51(-08), William James:
[...]
>
> Children need to be changed before the parent:
>
> ruby -e 'Dir["**/*"].reverse.each{|x|File.rename(x,x.downcase)}'
Not yet, you need to downcase the basename only.
$ find .
..
../B
../B/C
../B/C/D
$ ruby -e 'Dir["**/*"].reverse.each{|x|File.rename(x,x.downcase)}'
-e:1:in `rename': No such file or directory - B/C/D or b/c/d (Errno::ENOENT)
from -e:1
from -e:1
--
Stéphane
| |
| William James 2006-01-24, 8:56 pm |
| Stephane CHAZELAS wrote:
> 2006-01-24, 02:51(-08), William James:
> [...]
>
> Not yet, you need to downcase the basename only.
>
> $ find .
> .
> ./B
> ./B/C
> ./B/C/D
> $ ruby -e 'Dir["**/*"].reverse.each{|x|File.rename(x,x.downcase)}'
> -e:1:in `rename': No such file or directory - B/C/D or b/c/d (Errno::ENOE=
NT)
> from -e:1
> from -e:1
>
> --
> St=E9phane
ruby -e 'Dir["**/*"].reverse.each{|x|
File.rename(x, File.dirname(x)+"/"+File.basename(x).downcase)}'
|
|
|
|
|