mgChristopher
mgChristopher

Reputation: 375

Bash remove forward slash

In my bash script I have a variable:

appPath="/Payload/coolApp.app"

I want to create a string containing:

"build/ios/coolApp/"

I figured I could use bash string replacement operator:

${string/substring/replacement}

Unfortunately I discovered that it doesn't remove forward slashes inside a substring.

Example script:

appPath="/Payload/coolApp.app"

appName=${appPath/"/.app"/}
appName=${appPath/"/Payload"/}

echo "build/ios/$appName/"

My output becomes this:

build/ios//Payload/coolApp.app/

My Question: Why doesn't it remove the forwards slashes? NOTE: I am currently running on MacOSX 10.11.6

Testing solution scripts:

#!/bin/sh

appPath="/Payload/coolApp.app"

appName=${appPath/"/.app"/}
appName=${appPath/"/Payload"/}
echo "Original:" "build/ios/$appName/"


appName=${appPath/"\/.app"/}
appName=${appPath/"\/Payload"/}
echo "Escape \:" "build/ios/$appName/"


v1="${appPath/\/Payload/build\ios}"
echo "heemayl:" $v1


v2="${appPath##/Payload/}"
v2="build/ios/${appPath%.app}/"
echo "redneb:" $v2

Output:

Original: build/ios//Payload/coolApp.app/
Escape \: build/ios//Payload/coolApp.app/
heemayl: build/ios/coolApp.app
redneb: build/ios//Payload/coolApp/

Upvotes: 1

Views: 4030

Answers (6)

user8017719
user8017719

Reputation:

There are several ways to get replacement to work:

a='/Payload/coolApp.app'

echo "simple: ${a///Payload/build/ios}"
echo "plain : ${a//'/Payload'/build/ios}"
echo "front : ${a/#'/Payload'/build/ios}"
echo "end   : build/ios/${a##*/}"

When executed they will print:

simple: build/ios/coolApp.app
plain : build/ios/coolApp.app
front : build/ios/coolApp.app
end   : build/ios/coolApp.app

The simple option just gives as many / as the parameter expansion needs.
But it is confusing.

The plain option does almost the same, but quotes the pattern, which makes it a lot more readable.

The front option use an # (front of string) to end the need for /.

The end option is not a replacement, but a "Remove matching prefix pattern".
It removes everything up to the last /. Then, the needed text is prepended.


But a more useful way to write this is using variables:

a='/Payload/coolApp.app'

b='/Payload/'
c='build/ios/'

a=${a/"$b"/$c}"
echo "var   : ${a}"

Which will print:

var   : build/ios/coolApp.app

Use the variables to make repeated changes:

b='/Payload/' c='build/ios/'
a=${a/"$b"/$c}
echo "var1  : ${a}"

b='.app' c='/'
a=${a/"$b"/$c}
echo "var2  : ${a}"

Which will print:

var1  : build/ios/coolApp.app
var2  : build/ios/coolApp/

Upvotes: 1

l'L'l
l'L'l

Reputation: 47169

Using BASH_REMATCH can come in handy for this type of scenario:

appPath="/Payload/coolApp.app"
pattern='/.*/(.*)[.].*'

[[ $appPath =~ $pattern ]] && echo "build/ios/${BASH_REMATCH[1]}/"

Output:

build/ios/coolApp/

The double bracket statement [[ $appPath =~ $pattern ]] acts as an IF statement, which essentially asks itself "does $appPath fit the $pattern?" — if it does then echo the substitution.

Here's a summary of what the regex pattern does:

/.*/ matches /Payload/

  / matches the character / literally
  .* matches any character (except newline)
   |_ Quantifier: * Between zero and unlimited times
    / matches the character / literally

(.*) matches coolApp and captures it to BASH_REMATCH[1] ( capture group 1 )

  1st Capturing group (.*)
  .* matches any character (except newline)
   |_ Quantifier: * Between zero and unlimited times

[.].* matches .app and anything else to the end of line

  [.] match a single character ( character class )
   |_ . the literal character .
      .* matches any character (except newline)
       |_ Quantifier: * Between zero and unlimited times

It may seem complicated, although once you understand what the pattern is doing it starts to make sense. Being able to visualize the pattern within a regex editor is often helpful for learning also.

Upvotes: 3

John Bollinger
John Bollinger

Reputation: 180201

I figured I could use bash string replacement operator:

${string/substring/replacement}

Unfortunately I discovered that it doesn't remove forward slashes inside a substring.

But of course it does, or at least can do. You simply need to distinguish the slashes that are part of the substitution syntax from those that are part of the data:

appPath=/Payload/coolApp.app

echo ${appPath/\/Payload/build/ios}

Note in particular that only the first slash or double slash and the second slash are significant to the substitution syntax. Any subsequent slashes, such as the one between "build" and "ios" in the example, are just data.

Furthermore, you are getting a bit tripped up by quotation. This form will output what you want, too:

echo ${appPath/\/Payload/build\/ios}

but this will not:

echo "${appPath/\/Payload/build\/ios}"

That's because the second backslash in the substitution is also just data, so the substitution produces build/ios\/coolApp/. When that appears outside double quotes, the backslash is treated as a quote character to be removed during the subsequent quote-removal step. When it appears inside double quotes, it is itself a quote character only when followed by a character that needs escaping in that context ($, `, ", or \), or by a newline. In your case, therefore, it is just data, so it is not removed during the quote removal phase of command-line expansion.

More generally, you are using quotes in a lot of places where you do not need to do. Unlike many languages, the shell language does not rely on quotation to distinguish strings from other syntactic elements. In fact, it has very little distinction of that kind. You need to use quotation only to suppress what would otherwise be a special meaning of one or more characters.

Upvotes: 2

chepner
chepner

Reputation: 531135

If you only want the app name, use

appName=${appPath##*/}  # Drop everything up to and including the final /
appName=${appName%.app} # Drop the extension

Upvotes: 0

redneb
redneb

Reputation: 23850

Try this:

appPath="/Payload/coolApp.app"
appPath="${appPath##/Payload/}"
appPath="build/ios/${appPath%.app}/"
echo "$appPath"

it prints:

build/ios/coolApp/

Upvotes: 0

heemayl
heemayl

Reputation: 42017

You need to escape / in the string with \:

$ appPath="/Payload/coolApp.app"

$ echo "${appPath/\/Payload/build\/ios}"
build/ios/coolApp.app

Upvotes: 2

Related Questions