3

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

Using Bash, OSX.

8 Answers 8

4

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
$ 
4
  • This is what I was looking for. How can I use it in a function to operate on $1: function format { sed -e 's/-/ /g' -e 's/\b\(.\)/\u\1/g' } Commented Dec 9, 2014 at 2:01
  • This is not portable syntax - I think even the OSX version of sed will have difficulties w/ \b - I could be wrong about that though. Commented Dec 9, 2014 at 4:49
  • 3
    Apparently this doesn't work on OSX. I get foo uar uaz nonce. Commented Dec 9, 2014 at 10:51
  • On OSX, look into installing gsed and using that instead. Works perfectly. Commented Aug 23, 2022 at 16:42
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
4
  • @don_crissti: output fixed. Commented Dec 9, 2014 at 19:04
  • printf '%s\n' "${words[@]^}" | paste -sd ' ' is easier done with printf '%s\n' "${words[*]^}" Commented Dec 9, 2014 at 19:17
  • @1_CR: you are wrong, OP want 'Foo Bar' not 'Foo\nBar' Commented Dec 9, 2014 at 20:04
  • @sputnick, give it a shot Commented Dec 9, 2014 at 20:14
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.

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
2
  • I need to pass it an arbitrary string, and convert it to spaces and capitalized. Not just "foo-bar". Commented Dec 9, 2014 at 0:28
  • @BSeven Then you should ask your question that way next time. Commented Dec 9, 2014 at 0:49
0

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
5
  • This looks good but I got foo Ubar foo Ubar. Commented Dec 9, 2014 at 10:48
  • Did you just miss backslash? Commented Dec 9, 2014 at 10:53
  • No, I copied and pasted: echo "foo-bar-foo-bar" | tr "-" " " | sed -e 's/\b./\U&/g' Commented Dec 9, 2014 at 10:56
  • I also copied and pasted the output from my terminal. I am using RHEL 6.5. Commented Dec 9, 2014 at 10:57
  • 1
    Apparently there are some differences between OSX and Linux: unix.stackexchange.com/questions/13711/… Commented Dec 9, 2014 at 10:59
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
0
newstring=$(
    IFS=-; set -f
    array=($string)
    IFS=' '
    printf '%s' "${array[*]^}"
)

This first splits the string in $string on dashes, creating an array. To do this, we use $string unquoted with $IFS set to a dash character. We also disable filename globbing with set -f to avoid accidentally picking up any filenames if the string is split into something resembling a filename globbing pattern.

We then concatenate the elements of the array with a space character as the delimiter and, at the same time, upper-case the first character of each element. The upper-casing of each array element is done using {$array[*]^}. This creates a single string from the elements of the array, delimited by the space in $IFS.

Modifying the special IFS variable and setting set -f to avoid globbing are local operations in the subshell that constitute the command substitution.

0

For bash you can use sed and tr:

$ echo "foo-bar" | sed -E "s/[[:alnum:]]+/\u&/g" | tr '-' ' ' 
Foo Bar
1
  • 1
    Note that the default sed on macOS (which is what the user in the question is using) does not support \u to upper-case the replacement string in the s command. The command that you show generates the string ufoo ubar. Commented Apr 8, 2024 at 14:47

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.