Reputation: 375
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
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
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
(.*)
matchescoolApp
and captures it toBASH_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
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
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
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
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