22

Is there a way to convert something like this:

MyDirectoryFileLine

to

my-directory-file-line

I found some ways to convert all letters to uppercase or lowercase, but not in that way; any ideas?

GreenMatt
  • 18,244
  • 7
  • 53
  • 79
armandfp
  • 1,059
  • 1
  • 9
  • 18
  • To go from spinal case to camel case, you can reference: http://stackoverflow.com/questions/34420091/spinal-case-to-camel-case#34420162 – James Oravec Dec 22 '15 at 20:28

9 Answers9

30

You can use s/\([A-Z]\)/-\L\1/g to find an upper case letter and replace it with a dash and it's lower case. However, this gives you a dash at the beginning of the line, so you need another sed expression to handle that.

This should work:

sed --expression 's/\([A-Z]\)/-\L\1/g' \
    --expression 's/^-//'              \
    <<< "MyDirectoryFileLine"
bschlueter
  • 3,817
  • 1
  • 30
  • 48
dogbane
  • 266,786
  • 75
  • 396
  • 414
14

I propose to use sed to do that:

NEW=$(echo MyDirectoryFileLine        \
     | sed 's/\(.\)\([A-Z]\)/\1-\2/g' \
     | tr '[:upper:]' '[:lower:]')

UPD I forget to convert to lower case, updated code

bschlueter
  • 3,817
  • 1
  • 30
  • 48
4ndrew
  • 15,354
  • 2
  • 27
  • 29
8
echo MyDirectoryFileLine | perl -ne 'print lc(join("-", split(/(?=[A-Z])/)))'

prints my-directory-file-line

eumiro
  • 207,213
  • 34
  • 299
  • 261
4

Slight variation on @bilalq's answer that covers some more possible edge cases:

echo "MyDirectoryMVPFileLine"                          \
| sed 's/\([^A-Z]\)\([A-Z0-9]\)/\1-\2/g'               \
| sed 's/\([A-Z0-9]\)\([A-Z0-9]\)\([^A-Z]\)/\1-\2\3/g' \
| tr '[:upper:]' '[:lower:]'

output is still:

my-directory-mvp-file-line

but also:

WhatADeal -> what-a-deal
TheMVP -> the-mvp
DoSomeABTesting -> do-some-ab-testing
The3rdThing -> the-3rd-thing
The3Things -> the-3-things
ThingNumber3 -> thing-number-3
Peter
  • 197
  • 1
  • 11
2

None of the solutions posted here worked for me. Most didn't support multiple platforms well. The one from @4ndrew was close, but it failed on edge cases that had multiple capitalized characters next to each other (example: FooMVPClient turns into foo-mv-pclient instead of foo-mvp-client).

This worked for me:

echo "MyDirectoryMVPFileLine"              \
| sed 's/\([a-z]\)\([A-Z]\)/\1-\2/g'       \
| sed 's/\([A-Z]\{2,\}\)\([A-Z]\)/\1-\2/g' \
| tr '[:upper:]' '[:lower:]'

output:

my-directory-mvp-file-line
bschlueter
  • 3,817
  • 1
  • 30
  • 48
bilalq
  • 7,279
  • 3
  • 22
  • 28
2

My modest contribution that works with "/" (possible use for directory names or github repo names). It's not as clean as it could be, but does the job. I've used @Peter contribution as a base, then tweaked a bit.

function kebab_case() {
  echo -n "$1" |\
  sed 's/\([^A-Z+]\)\([A-Z0-9]\)/\1-\2/g' |\
  sed 's/\([0-9]\)\([A-Z]\)/\1-\2/g' |\
  sed 's/\([A-Z]\)\([0-9]\)/\1-\2/g' |\
  sed 's/--/-/g' |\
  sed 's/\([\/]\)-/\1/g' |\
  tr '[:upper:]' '[:lower:]'
}

function assert_kebab_equal() {
    local Actual
    local Expected
    Expected="$1"
    Actual="$(kebab_case "$2")"

    if [ "${Expected}" != "${Actual}" ]; then
      echo Error:
      echo "  Actual: ${Actual}"
      echo "Expected: ${Expected}"
    else
      echo "$2" "$1" | awk '{ printf "%-30s -> %-40s\n", $1, $2}'
    fi
}

assert_kebab_equal "abc-def" "AbcDef"
assert_kebab_equal "/abc-def-ghi/def" "/AbcDef-Ghi/Def"
assert_kebab_equal "/ab-cd-ef" "/AbCdEf"
assert_kebab_equal "repo-owner/repo-name" "RepoOwner/RepoName"
assert_kebab_equal "repo-12-owner/repo-12-name" "Repo12Owner/Repo12Name"
assert_kebab_equal "repo-12-3-owner/repo-12-name" "Repo12-3Owner/Repo12Name"
assert_kebab_equal "repo-owner/repo-name" "REPO-OWNER/REPO-NAME"
assert_kebab_equal "repo-owner-2/repo-name" "REPO-OWNER2/REPO-NAME"
assert_kebab_equal "repo-1-owner" "REPO1-OWNER"
assert_kebab_equal "repo-1-owner-1/22-repo-2-name" "REPO1-OWNER1/22REPO-2NAME"


# Outputs:

AbcDef                         -> abc-def                                 
/AbcDef-Ghi/Def                -> /abc-def-ghi/def                        
/AbCdEf                        -> /ab-cd-ef                               
RepoOwner/RepoName             -> repo-owner/repo-name                    
Repo12Owner/Repo12Name         -> repo-12-owner/repo-12-name              
Repo12-3Owner/Repo12Name       -> repo-12-3-owner/repo-12-name            
REPO-OWNER/REPO-NAME           -> repo-owner/repo-name                    
REPO-OWNER2/REPO-NAME          -> repo-owner-2/repo-name                  
REPO1-OWNER                    -> repo-1-owner                            
REPO1-OWNER1/22REPO-2NAME      -> repo-1-owner-1/22-repo-2-name         

Ludovic C
  • 2,855
  • 20
  • 40
0
echo "SomeACRONYMInCamelCaseString" \
 | sed -e 's/\([a-z]\)\([A-Z]\)/\1-\L\2/' \
 | sed -e 's/\(.*\)/\L\1/')

sed -e 's/\([a-z]\)\([A-Z]\)/\1-\L\2/' replace an uppercase letter with a hyphen and a lowercase letter only if it is preceded by a lowercase letter. sed -e 's/\(.*\)/\L\1/' puts the whole string in lowercase

lorjuelag
  • 53
  • 1
  • 7
0

With GNU sed:

echo "MyDirectoryFileLine"|sed -e 's/\([A-Z]\)/-\L\1/g'

You just need to strip the first dash if that's bothers you:

echo "MyDirectoryFileLine"|sed -e 's/\([A-Z]\)/-\L\1/g' -e 's/^-//'

With BSD sed it it's a bit longer:

echo "MyDirectoryFileLine"|sed -e 's/\([A-Z]\)/-\1/g' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' -e 's/^-//'

Update: the BSD version will work with the GNU version, so I recommend using the latter.

KARASZI István
  • 30,900
  • 8
  • 101
  • 128
0

This might work for you:

<<<"MyDirectoryFileLine" sed 's/[A-Z]/-\l&/g;s/.//'
my-directory-file-line
potong
  • 55,640
  • 6
  • 51
  • 83