3

Given the string foo-bar, what is a good way to convert it to Foo Bar?

Using Bash, OSX.

B Seven
  • 7,989

6 Answers6

5

Start by changing dashes to spaces, like: sed 's/-/ /g'

Uppercasing the first letter is already solved (go upvote them; or just mark this question a duplicate of that one).

Then combine them: sed -e 's/-/ /g' -e 's/\b\(.\)/\u\1/g'

$ echo "foo-bar-baz-nonce" | sed -e 's/-/ /g' -e 's/\b\(.\)/\u\1/g'
Foo Bar Baz Nonce
$ 
2

One solution using , not restricted to only `foo-bar`

$ cat file
aaa-zzz-eee-rrr
foo-bar

code

$ perl -ne 'print join " ", map { ucfirst } split /-/' file
Aaa Zzz Eee Rrr
Foo Bar

Another solution using pure bash

while IFS='-' read -r -a words; do
    printf '%s\n' "${words[@]^}" | paste -sd ' '
done < file

Output:

Aaa Zzz Eee Rrr 
Foo Bar
2

For the splitting in a POSIX shell you could do:

set -f; IFS=-; set -- $1; IFS=' '

There - now all of your dashes are spaces and you can have the entire string in "$*" or else each space - previously dash - separated string in $1 $2 $3 ... (the total count of which is available to you in "$#") or you can get them as an expandable argument list in "$@".

Now, for the case conversion you can do:

IFS='
'; for c in $(printf %.1s "$@" | dd cbs=1 conv=unblock,ucase)
do set -- "$@" "$c${1#[!"$IFS"]}"; shift; done

Here's the whole thing by example:

set -f -- aaa-zzz-eee-rrr-foo-bar
IFS=-; set -- $1; IFS=' 
'; for c in $(printf %.1s "$@" | dd cbs=1 conv=unblock,ucase)
do set -- "$@" "$c${1#?}"
shift; done; printf %s\\n "$@"

which prints:

Aaa
Zzz
Eee
Rrr
Foo
Bar

...which isn't space-separated I know - I used "$@". It is space-separated in "$*" because I set $IFS's first byte to space - the delimiter is definable in that way. For example:

IFS=k; printf %s\\n "$*"; IFS=' '; printf %s\\n "$*"

...which prints...

AaakZzzkEeekRrrkFookBar
Aaa Zzz Eee Rrr Foo Bar

You can save it any time of course:

IFS=-; dashes="$*"; IFS=' '; spaces="$*"; IFS=; empty="$*"
printf %s\\n "$dashes" "$spaces" "$empty"

...which prints:

Aaa-Zzz-Eee-Rrr-Foo-Bar
Aaa Zzz Eee Rrr Foo Bar
AaaZzzEeeRrrFooBar

This is a more complex subject but it can be used to great effect in these areas. It is important to protect data integrity though - use set -f when splitting in the shell to avoid filename generation on [?* and quote expansions you don't intend to split.

The dd command above - as it should be noted - is likely only to be very effective in an ASCII locale. In others you should probably look to recode or iconv or similar.

mikeserv
  • 58,310
1

Step 1: Replace the - with a space

For this you can use tr command as follows:

tr "-" " "

Step 2: Make first letter of every word capital

For this, you can find the word boundary using \b and make immediate letter capital. The . represents immediate letter after word boundary \b, \U makes it capital and & will keep all other letters in the letters as they were.

sed -e 's/\b./\U&/g'

Check:

echo "foo-bar-foo-bar" | tr "-" " " | sed -e 's/\b./\U&/g'
Foo Bar Foo Bar
0

If that string is part of a file or output:

sed 's/foo-bar/Foo Bar/g' file

Assuming there are no "strange" characters in the string (/, \, &):

string="foo-bar"
set -- $(echo "${string//-/ }")
replstr="$(for word in $*; do echo -n "${word^} "; done)"
replstr="${replstr% }"
sed "s/${string}/${replstr}/g' file
Hauke Laging
  • 90,279
0

in bash

v=foo-bar && v=$(IFS=- read -ra x <<<"$v"; printf '%s' "${x[*]^}") && printf '%s\n' "$v"
Foo Bar

In python

v=foo-bar python -c 'import os, string
print string.capwords(" ".join(os.environ["v"].split("-")))
'
Foo Bar
iruvar
  • 16,725