zsh-users
 help / color / mirror / code / Atom feed
* more fun with parameter expansion
@ 2000-06-16 18:53 Clint Adams
  2000-06-16 19:54 ` Clint Adams
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Clint Adams @ 2000-06-16 18:53 UTC (permalink / raw)
  To: zsh-users

This is for someone who wants to take a directory tree and convert all
the filenames (and directory names) to lowercase, replacing spaces
with underscores.  It presumes that you are using GNU mv and have
MARK_DIRS set.  I'm sure that someone can make it more efficient.

for i (*(/N)) {mv -v "$i" "${(L)i:gs/ /_/}"}
for i (*/**/*(/N)) {mv -v "${${(L)i%/*/}:gs/ /_/}/${${${i#*/}%/}##*/}" "${(L)i:gfor i (**/*(N^/)) {mv -v "${i}" "${(L)i:gs/ /_/}"}


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: more fun with parameter expansion
  2000-06-16 18:53 more fun with parameter expansion Clint Adams
@ 2000-06-16 19:54 ` Clint Adams
  2000-06-16 21:00 ` Bart Schaefer
  2000-06-18 22:02 ` Peter Stephenson
  2 siblings, 0 replies; 8+ messages in thread
From: Clint Adams @ 2000-06-16 19:54 UTC (permalink / raw)
  To: zsh-users

> for i (*(/N)) {mv -v "$i" "${(L)i:gs/ /_/}"}
> for i (*/**/*(/N)) {mv -v "${${(L)i%/*/}:gs/ /_/}/${${${i#*/}%/}##*/}" "${(L)i:gfor i (**/*(N^/)) {mv -v "${i}" "${(L)i:gs/ /_/}"}
> 

Hmm... I'll space them this time.

for i (*(/N)) {mv -v "$i" "${(L)i:gs/ /_/}"}

for i (*/**/*(/N)) {mv -v "${${(L)i%/*/}:gs/ /_/}/${${${i#*/}%/}##*/}" "${(L)i:gs/ /_/}"}

for i (**/*(N^/)) {mv -v "${i}" "${(L)i:gs/ /_/}"}


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: more fun with parameter expansion
  2000-06-16 18:53 more fun with parameter expansion Clint Adams
  2000-06-16 19:54 ` Clint Adams
@ 2000-06-16 21:00 ` Bart Schaefer
  2000-06-16 21:44   ` Bart Schaefer
  2000-06-18 22:02 ` Peter Stephenson
  2 siblings, 1 reply; 8+ messages in thread
From: Bart Schaefer @ 2000-06-16 21:00 UTC (permalink / raw)
  To: Clint Adams, zsh-users

On Jun 16,  2:53pm, Clint Adams wrote:
> Subject: more fun with parameter expansion
> This is for someone who wants to take a directory tree and convert all
> the filenames (and directory names) to lowercase, replacing spaces
> with underscores.  It presumes that you are using GNU mv and have
> MARK_DIRS set.  I'm sure that someone can make it more efficient.

I dunno about efficient, but you can do it with only one glob and one
loop, and it doesn't need to care about MARK_DIRS.

Oh, and your solution missed file names that began with a dot, unless you
meant to say that both MARK_DIRS and GLOB_DOTS were needed ...

for f in **/*(DNon); do
  mv -v $f ${${(M)f%/*}:+${${f%/*}:l:gs/ /_/}}${${${(M)f%/*}:-$f}:l:gs/ /_/}
done

The `(on)' (order by name) is probably unnecessary, and won't work in 3.0.x
(though everything else should).

Writing that one-liner out a bit longer might make this more readable:

  t=${(M)f%/*}     # The tail of the path, including leading slash
  h=${t:+${f%/*}}  # The head of the path if the tail is non-empty
  t=${t:-$f}       # The tail is the path when the tail is empty

  h=$h:l           # Downcase the head, same as ${(L)h}
  t=$t:l           # Downcase the tail

  h=$h:gs/ /_/
  t=$h:gs/ /_/

  mv $f $h$t

The last five steps could be written as

  mv $f ${${:-$h$t}:l:gs/ /_/}

Do you see what that's doing?  Exercise: Rewrite the "mv" in my for-loop
with that trick, to make it at least 8 characters shorter.

Incidentally, you can turn on MARK_DIRS for individual globs like this:

	echo *(M)


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: more fun with parameter expansion
  2000-06-16 21:00 ` Bart Schaefer
@ 2000-06-16 21:44   ` Bart Schaefer
  2000-06-17  1:17     ` Clint Adams
  0 siblings, 1 reply; 8+ messages in thread
From: Bart Schaefer @ 2000-06-16 21:44 UTC (permalink / raw)
  To: Clint Adams, zsh-users

On Jun 16,  2:00pm, Bart Schaefer wrote:
> Subject: Re: more fun with parameter expansion
> 
> for f in **/*(DNon); do
>   mv -v $f ${${(M)f%/*}:+${${f%/*}:l:gs/ /_/}}${${${(M)f%/*}:-$f}:l:gs/ /_/}
> done
> 
> The `(on)' (order by name) is probably unnecessary, and won't work in 3.0.x
> (though everything else should).

Of course I completely forgot that the whole reason I split the thing into
head and tail was because this needs to work in REVERSE name order.  Sigh.

The correct solution is

for f in **/*(DNOn^M); do
  mv -v $i ${${(M)i%/*}:+${i%/*}}${${${(M)i%/*}:-$i}:l:gs/ /_/}
done

(note one fewer :l:gs and turn *off* markdirs) which *won't* work in 3.0.x
because of no (On) glob flag.

> Writing that one-liner out a bit longer might make this more readable:
> 
>   t=${(M)f%/*}     # The tail of the path, including leading slash
>   h=${t:+${f%/*}}  # The head of the path if the tail is non-empty
>   t=${t:-$f}       # The tail is the path when the tail is empty
> 
>   h=$h:l           # Downcase the head, same as ${(L)h}
>   h=$h:gs/ /_/

This was my error; don't modify the head, instead wait for it to become
the tail (that's why the reverse ordering) and then modify it.

>   t=$t:l           # Downcase the tail
>   t=$h:gs/ /_/

And I forgot that braces are needed because of the space:

    t=${t:gs/ /_/}

>   mv $f $h$t
> 
> The last five steps could be written as
> 
>   mv $f ${${:-$h$t}:l:gs/ /_/}

Rather:

    mv $f $h${t:l:gs/ /_/}

which isn't nearly as mysterious.  Oh well.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: more fun with parameter expansion
  2000-06-16 21:44   ` Bart Schaefer
@ 2000-06-17  1:17     ` Clint Adams
  0 siblings, 0 replies; 8+ messages in thread
From: Clint Adams @ 2000-06-17  1:17 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: zsh-users

> Oh, and your solution missed file names that began with a dot, unless you
> meant to say that both MARK_DIRS and GLOB_DOTS were needed ...

No, I didn't.  I hadn't considered dotfiles, actually.

> The correct solution is
> 
> for f in **/*(DNOn^M); do
>   mv -v $i ${${(M)i%/*}:+${i%/*}}${${${(M)i%/*}:-$i}:l:gs/ /_/}
> done

Of course, you meant to change all the i's to f's or the f to an i.

Thanks for the shorter version.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: more fun with parameter expansion
  2000-06-16 18:53 more fun with parameter expansion Clint Adams
  2000-06-16 19:54 ` Clint Adams
  2000-06-16 21:00 ` Bart Schaefer
@ 2000-06-18 22:02 ` Peter Stephenson
  2000-06-19  9:08   ` Peter Stephenson
  2 siblings, 1 reply; 8+ messages in thread
From: Peter Stephenson @ 2000-06-18 22:02 UTC (permalink / raw)
  To: zsh-users

Clint Adams wrote:
> This is for someone who wants to take a directory tree and convert all
> the filenames (and directory names) to lowercase, replacing spaces
> with underscores.  It presumes that you are using GNU mv and have
> MARK_DIRS set.  I'm sure that someone can make it more efficient.
> 
> for i (*(/N)) {mv -v "$i" "${(L)i:gs/ /_/}"}
> for i (*/**/*(/N)) {mv -v "${${(L)i%/*/}:gs/ /_/}/${${${i#*/}%/}##*/}" "${(L)
> i:gfor i (**/*(N^/)) {mv -v "${i}" "${(L)i:gs/ /_/}"}

See zmv in the Functions/Misc directory of 3.1.9 (this version is needed
for it to work).

zmv '(**/)(*[ A-Z]*)' '${1}${(L)2/ /_}'

works on a simple test case (use the option -n just to test what it would
do).  Each parenthesis maps to a positional parameter; (**/) is a special
case and is the only sort of multi-directory pattern that works.  Looks
like zmv needs the option to ignore files whose names didn't change (it's a
pain having to make the LH pattern so specific).  I'd appreciate any
feedback about zmv.

-- 
Peter Stephenson <pws@pwstephenson.fsnet.co.uk>
Work: pws@CambridgeSiliconRadio.com
Web: http://www.pwstephenson.fsnet.co.uk


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: more fun with parameter expansion
  2000-06-18 22:02 ` Peter Stephenson
@ 2000-06-19  9:08   ` Peter Stephenson
  2000-06-19 15:21     ` Clint Adams
  0 siblings, 1 reply; 8+ messages in thread
From: Peter Stephenson @ 2000-06-19  9:08 UTC (permalink / raw)
  To: Zsh users list

I wrote:
> Clint Adams wrote:
> > This is for someone who wants to take a directory tree and convert all
> > the filenames (and directory names) to lowercase, replacing spaces
> > with underscores.
> 
> See zmv in the Functions/Misc directory of 3.1.9 (this version is needed
> for it to work).
> 
> zmv '(**/)(*[ A-Z]*)' '${1}${(L)2/ /_}'
> 
> works on a simple test case (use the option -n just to test what it would
> do).

However, it didn't do depth-first matching, so things like `MYDIR/MYFILE'
were screwed up because it tried to rename MYDIR to mydir, then
MYDIR/MYFILE (which didn't exist) to MYDIR/myfile.

Luckily, there are now glob qualifiers to do depth-first ordering: **/*(odon)
orders first by depth under a given directory, then by name.

> Looks
> like zmv needs the option to ignore files whose names didn't change (it's a
> pain having to make the LH pattern so specific).

I've made this the default.  I can't see why you would want the error.

Now you should be able to do

  zmv '(**/)(*)' '${1}${(L)2// /_}'

(I should have put the doubled slash in last time).

I'm sending this to zsh-users, because you can always copy an installed zmv
and patch it yourself.

Index: Functions/Misc/zmv
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/Misc/zmv,v
retrieving revision 1.2
diff -u -r1.2 zmv
--- Functions/Misc/zmv	2000/05/05 14:14:12	1.2
+++ Functions/Misc/zmv	2000/06/19 09:02:41
@@ -61,10 +61,11 @@
 # other words, parenthesising of wildcards is independent of any existing
 # parentheses.
 #
-# Any error --- a substitution resulted in an empty string, a
-# substitution did not change the file name, two substitutions gave the
-# same result, the destination was an existing regular file and -f was not
-# given --- causes the entire function to abort without doing anything.
+# Any file whose name is not changed by the substitution is simply ignored.
+# Any error --- a substitution resulted in an empty string, two
+# substitutions gave the same result, the destination was an existing
+# regular file and -f was not given --- causes the entire function to abort
+# without doing anything.
 #
 # Options:
 #  -f  force overwriting of destination files.  Not currently passed
@@ -76,7 +77,8 @@
 #  -n  no execution: print what would happen, but don't do it.
 #  -q  Turn bare glob qualifiers off:  now assumed by default, so this
 #      has no effect.
-#  -Q  Force bare glob qualifiers on.
+#  -Q  Force bare glob qualifiers on.  Don't turn this on unless you are
+#      actually using glob qualifiers in a pattern (see below).
 #  -s  symbolic, passed down to ln; only works with zln or z?? -L.
 #  -v  verbose: print line as it's being executed.
 #  -o <optstring>
@@ -119,7 +121,7 @@
 local f g args match mbegin mend files action myname tmpf opt exec
 local opt_f opt_i opt_n opt_q opt_Q opt_s opt_M opt_C opt_L 
 local opt_o opt_p opt_v opt_w MATCH MBEGIN MEND
-local pat repl errstr fpat
+local pat repl errstr fpat hasglobqual opat
 typeset -A from to
 integer stat
 
@@ -190,17 +192,29 @@
   fi
 fi
 
+if [[ -n $opt_Q && $pat = (#b)(*)\([^\)\|\~]##\) ]]; then
+  hasglobqual=q
+  # strip off qualifiers for use as ordinary pattern
+  opat=$match[1]
+fi
+
 if [[ $pat = (#b)(*)\((\*\*##/)\)(*) ]]; then
   fpat="$match[1]$match[2]$match[3]"
+  # Now make sure we do depth-first searching.
+  # This is so that the names of any files are altered before the
+  # names of the directories they are in.
+  if [[ -n $opt_Q && -n $hasglobqual ]]; then
+    fpat[-1]="odon)"
+  else
+    setopt bareglobqual
+    fpat="${fpat}(odon)"
+  fi
 else
   fpat=$pat
 fi
 files=(${~fpat})
 
-if [[ -o bareglobqual && $pat = (#b)(*)\([^\)\|\~]##\) ]]; then
-  # strip off qualifiers for use as ordinary pattern
-  pat=$match[1]
-fi
+[[ -n $hasglobqual ]] && pat=$opat
 
 errs=()
 
@@ -219,7 +233,9 @@
   if [[ -z $g ]]; then
     errs=($errs "$f expanded to empty string")
   elif [[ $f = $g ]]; then
-    errs=($errs "$f not altered by substitution")
+    # don't cause error: more useful just to skip
+    #   errs=($errs "$f not altered by substitution")
+    continue
   elif [[ -n $from[$g] && ! -d $g ]]; then
     errs=($errs "$f and $from[$g] both map to $g")
   elif [[ -f $g && -z $opt_f ]]; then

-- 
Peter Stephenson <pws@cambridgesiliconradio.com>
Cambridge Silicon Radio, Unit 300, Science Park, Milton Road,
Cambridge, CB4 0XL, UK                          Tel: +44 (0)1223 392070


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: more fun with parameter expansion
  2000-06-19  9:08   ` Peter Stephenson
@ 2000-06-19 15:21     ` Clint Adams
  0 siblings, 0 replies; 8+ messages in thread
From: Clint Adams @ 2000-06-19 15:21 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh users list

>   zmv '(**/)(*)' '${1}${(L)2// /_}'

MARK_DIRS raises its ugly head again.  Changing the odon's to
odon^M made it work as advertised.


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2000-06-19 15:22 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2000-06-16 18:53 more fun with parameter expansion Clint Adams
2000-06-16 19:54 ` Clint Adams
2000-06-16 21:00 ` Bart Schaefer
2000-06-16 21:44   ` Bart Schaefer
2000-06-17  1:17     ` Clint Adams
2000-06-18 22:02 ` Peter Stephenson
2000-06-19  9:08   ` Peter Stephenson
2000-06-19 15:21     ` Clint Adams

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).