Given the string foo-bar
, what is a good way to convert it to Foo Bar
?
Using Bash, OSX.
Given the string foo-bar
, what is a good way to convert it to Foo Bar
?
Using Bash, OSX.
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
$
$ cat file
aaa-zzz-eee-rrr
foo-bar
code
$ perl -ne 'print join " ", map { ucfirst } split /-/' file
Aaa Zzz Eee Rrr
Foo Bar
while IFS='-' read -r -a words; do
printf '%s\n' "${words[@]^}" | paste -sd ' '
done < file
Output:
Aaa Zzz Eee Rrr
Foo Bar
printf '%s\n' "${words[@]^}" | paste -sd ' '
is easier done with printf '%s\n' "${words[*]^}"
– iruvar
Dec 09 '14 at 19:17
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.
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
echo "foo-bar-foo-bar" | tr "-" " " | sed -e 's/\b./\U&/g'
– B Seven
Dec 09 '14 at 10:56
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
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
function format { sed -e 's/-/ /g' -e 's/\b\(.\)/\u\1/g' }
– B Seven Dec 09 '14 at 02:01sed
will have difficulties w/\b
- I could be wrong about that though. – mikeserv Dec 09 '14 at 04:49foo uar uaz nonce
. – B Seven Dec 09 '14 at 10:51gsed
and using that instead. Works perfectly. – Jack Kinsella Aug 23 '22 at 16:42