# UNIX help: grep, sed, tr, regular expression question

**URL:** <https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487>\
**Category:** Factual Questions\
**Created:** [November 20, 2001, 11:18pm UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487 "2001-11-20T23:18:51Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Joe\_Cool](https://avatars.discourse-cdn.com/v4/letter/j/ecb155/32.png) [@Joe\_Cool](https://boards.straightdope.com/u/Joe_Cool)\
**Post date:** [November 20, 2001, 11:18pm UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/1 "2001-11-20T23:18:51Z")

</div>

I’m trying to write a shell script to go through and automatically rename all my mp3’s to a standard naming scheme, one of the key points being that I want the first letter of each word capitalized. This is where I’m running into my first major crash.

I tried doing

```auto

for i in *.mp3; do echo $i|sed 's/^[a-z]/[A-Z]/';done

```

as a test to see if I could capitalize the first letter of the name of each file, but then as you all know, I ended up with “[A-Z]ameo - word up.mp3” as a sample filename. So I tried doing all sorts of things, none of which worked. sed y/// and tr, on the other hand, happily uppercase all the letters, but that’s just as annoying as all lowercase.

So I’m still experimenting, but any advice would be hugely appreciated.

---

<div class="post-metadata">

**Author:** ![Earthling](https://avatars.discourse-cdn.com/v4/letter/e/90db22/32.png) [@Earthling](https://boards.straightdope.com/u/Earthling)\
**Post date:** [November 21, 2001, 12:09am UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/2 "2001-11-21T00:09:34Z")

</div>

```auto

ls *.mp3 | grep -v "^[A-Z]" > oldnames
while read i
do
  echo $i | cut -c1 >> char1
  echo $i | cut -c2- >> char_remain
  cat char1 | tr '[a-z]' '[A-Z]' > char1.x
  paste char1.x char_remain | sed 's/ //' > newnames
done < oldnames
paste -d\; oldnames newnames > combnames
  #for some reason the plain-old tab delimiter
  #doesn't work for the following cut statemts
while read names
do
  oldna=`echo $names | cut -f1 -d\;`
  newna=`echo $names | cut -f2 -d\;`
mv $oldna $newna
done < combnames
rm combnames oldnames newnames char1 char_remain char1.x

```

It’s pretty tortured (I’m not a professional Unix programmer by any stretch), but it works.

---

<div class="post-metadata">

**Author:** ![Earthling](https://avatars.discourse-cdn.com/v4/letter/e/90db22/32.png) [@Earthling](https://boards.straightdope.com/u/Earthling)\
**Post date:** [November 21, 2001, 12:11am UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/3 "2001-11-21T00:11:49Z")

</div>

What the ??? Where did all the extra line spaces come from? 😕  
They weren’t there in preview, I swear! :o

---

<div class="post-metadata">

**Author:** ![Astroglide](https://avatars.discourse-cdn.com/v4/letter/a/6de8d8/32.png) [@Astroglide](https://boards.straightdope.com/u/Astroglide)\
**Post date:** [November 21, 2001, 12:23am UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/4 "2001-11-21T00:23:48Z")

</div>

… but off the top of my head, the regex you need is:

/^[a-z]{1}/[A-Z]/

---

<div class="post-metadata">

**Author:** ![John\_T.Conklin](https://sea3.discourse-cdn.com/straightdope/user_avatar/boards.straightdope.com/john_t.conklin/32/3307_2.png) [@John\_T.Conklin](https://boards.straightdope.com/u/John_T.Conklin)\
**Post date:** [November 21, 2001, 1:16am UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/5 "2001-11-21T01:16:24Z")

</div>

With awk:

```auto

ls -1 *.mp3 | gawk '{ print toupper(substr($1,1,1)) substr($1,2) }'

```

Or perl:

```auto

ls -1 *.mp3 | perl -n -e 'print ucfirst'

```

---

<div class="post-metadata">

**Author:** ![D\_Nice](https://avatars.discourse-cdn.com/v4/letter/d/59ef9b/32.png) [@D\_Nice](https://boards.straightdope.com/u/D_Nice)\
**Post date:** [November 21, 2001, 1:21am UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/6 "2001-11-21T01:21:35Z")

</div>

I thought you had your naming script working like months ago.

---

<div class="post-metadata">

**Author:** ![Joe\_Cool](https://avatars.discourse-cdn.com/v4/letter/j/ecb155/32.png) [@Joe\_Cool](https://boards.straightdope.com/u/Joe_Cool)\
**Post date:** [November 21, 2001, 3:04am UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/7 "2001-11-21T03:04:33Z")

</div>

> [@](#):
>
> \*Originally posted by D\_Nice \*  
> \*\*I thought you had your naming script working like months ago. \*\*

Yeah but that one only worked under very specific conditions: all it did was change artist - song.mp3 to artist – song.mp3

And if you remember, that one took me like an entire day to get working. hahaha

---

<div class="post-metadata">

**Author:** ![Perderabo](https://avatars.discourse-cdn.com/v4/letter/p/a183cd/32.png) [@Perderabo](https://boards.straightdope.com/u/Perderabo)\
**Post date:** [November 21, 2001, 2:53pm UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/8 "2001-11-21T14:53:02Z")

</div>

Here is my ksh solution:

```auto

#! /usr/bin/ksh
typeset -u upshift
for filename in *.mp3 ; do
    rest=${filename#?}
    char1=${filename%$rest}
    upshift=$char1
    echo mv $filename ${upshift}${rest}
done
exit 0

```

On preview, I am getting double spacing. I think the code feature is broken.

---

<div class="post-metadata">

**Author:** ![Joe\_Cool](https://avatars.discourse-cdn.com/v4/letter/j/ecb155/32.png) [@Joe\_Cool](https://boards.straightdope.com/u/Joe_Cool)\
**Post date:** [November 22, 2001, 4:41am UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/9 "2001-11-22T04:41:18Z")

</div>

Well, I finally got it, and I did it all using sed, including a piece I grabbed from the USENET archive on google.  
In case there’s anybody who’s as weak at shell scripting as I am, I’m posting my script here, fully commented. The part that does the actual work only takes up about 350 bytes, but I thought maybe somebody could learn from my trouble, so I spent 10 minutes explaining what I did (for all you guys who kick my ass at scripting, you aren’t the ones I wrote the comments for! ;))

Thanks much to all who posted help. Any further comment or criticism is welcome.

> [@](#):
>
> #!/bin/bash
> 
> for i in \*.mp3  
> do mv “$i” "`echo “$i” | sed ’
> 
> # This is all one big sed script:
> 
> # This part of the script was borrowed from USENET
> 
> # Most comments are mine (after studying it to see
> 
> # how it works!)
> 
> # normalize line: precede every line with a space
> 
> s/^/ /
> 
> # isolate every word, capitalise its ini, then reassemble
> 
> # it back in its context
> 
> ## append a translate table ($ is the “end of pattern” token)
> 
> s/$/aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ/
> 
> ## isolate & capitalise, using space, (, and ) as
> 
> ## word separators, looping until all words are done
> 
> :loop  
> s/(._[()])([a-z])(._\2)(.)/\1\4\3\4/  
> tloop
> 
> #explanation: (.\*[()])
> 
> # selects Everything from the beginning until
> 
> # it finds a space (the expression is greedy,
> 
> # so the effect is to select everything up to
> 
> # the last non capitalized word: it would match
> 
> # "Hello, this is " instead of just "Hello, ").
> 
> # the escaped parantheses: ( ) stores what
> 
> # matches in a variable called \1. The next
> 
> # one is \2, and so forth up to 9.
> 
> # ([a-z])
> 
> # matches the first single lowercase alpha
> 
> # character following the space and stores
> 
> # it (as \2)
> 
> # (.\*\2)
> 
> # searches the remainder of the string (including
> 
> # the translation table) and matches everything up
> 
> # to and including the letter stored as \2, storing
> 
> # this as \3
> 
> # (.)
> 
> # matches the very next character (uppercase of the matched letter!)
> 
> # and stores it as \4
> 
> # /\1\4\3\4/
> 
> # What the pattern is replaced by.
> 
> # delete the initial space from every line
> 
> s/ (.\*)/\1/
> 
> # delete the translate table
> 
> s/aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ$//
> 
> #convert all \_ to spaces and condense multiple spaces to a single one  
> s/\_/ /g  
> s/ / /g
> 
> # Extract the artist name (everything up to the first
> 
> # hyphen that is surrounded by spaces) and Title
> 
> # (everything after the hyphen and before the ‘.mp3’),
> 
> # then output it in the preferred format:
> 
> # Artist – Song Title.mp3
> 
> # This part I wrote myself, by trial and error. 🙂
> 
> s/(^._) –_ (._).mp.(._)/\1 – \2.mp3/  
> ’  
> `"  
> done

---

<div class="post-metadata">

**Author:** ![D\_Nice](https://avatars.discourse-cdn.com/v4/letter/d/59ef9b/32.png) [@D\_Nice](https://boards.straightdope.com/u/D_Nice)\
**Post date:** [November 22, 2001, 4:43am UTC](https://boards.straightdope.com/t/unix-help-grep-sed-tr-regular-expression-question/94487/10 "2001-11-22T04:43:33Z")

</div>

Detailed comments are the key to altering the script to meet changing needs. Great script. Undoubtedly you will end up wanting to change it in few months. Without those comments, you would end up started from scratch. Great job.
