bash find if a variable contains a symbol and escape it - bash

I have written a bash script for Mac OS X that gathers a lot of computer information and writes it to a MySQL database. Everything works fine except one thing. One of the pieces of information I gather is the Computer Name. Probably around 90% of the computers have an apostrophe in their names. The problem is that when I try to write the variable to MySQL it errors out due to the apostrophe.
Is there a way to find if a variable contains an apostrophe and if it does rewrite the variable with the escape character? I am sure I need to use sed but I am still pretty new to this.
For example if I have the following variable:
COMPUTER_NAME="Fred Flintstone's MacBook Air"
How can I change the variable to:
COMPUTER_NAME="Fred Flintstone\'s MacBook Air"
I can pass this off to perl of I need to but I would rather keep it in BASH.
I also don't want to rename the computers because there are over 300 of them.

This will escape apostrophes that are not already escaped:
${COMPUTER_NAME/[^\\]\'/\\\'}

Try doing this to escape what you want :
string="Hello, c'est bientôt l'été ?"
printf '%q\n' "$string"
Output
$'Hello, c\'est bient\303\264t l\'\303\251t\303\251 ?'

You can use Parameter expansion with substitution:
COMPUTER_NAME="${COMPUTER_NAME//\'/\'}"

Related

Replace a variable every where in a shell script

I need to write a shell script such that I have to read .sh script and find a particular variable (for example, Variable_Name="variable1") and take out is value(variable1).
In other shell script if Variable_Name is used I need to replace it with its Value(variable1)
A simple approach, to build on, might be:
assignment=$(echo 'Variable_Name="variable1"' | sed -r 's/Variable_Name=(.*)/\1/')
echo $assignment
"variable1"
Depending on variable type, the value might be quoted or not, quoted with single apostrophs or quotes. That might be neccessary (String with or without blanks) or superflous. Behind the assignment there might be furter code:
pi=3.14;v=42;
or a comment:
user=janis # Janis Joplin
it might be complicated:
expr="foobar; O'Reilly " # trailing blank important
But only you may know, how complicated it might get. Maybe the simple case is already sufficient. If the new script looks similar, it might work, or not:
targetV=INSERT_HERE; secondV=23
# oops: secondV accidnetally hidden:
targetV="foobar; O'Reilly " # trailing blank important; secondV=23
If the second script is under your control, you can prevent such problems easily. If source and target language are identical, what worked here should work there too.

how to edit url string with sed

My Linux repository file contain a link that until now was using http with a port number to point to it repository.
baseurl=http://host.domain.com:123/folder1/folder2
I now need a way to replace that URL to use https with no port or a different port .
I need also the possibility to change the server name for example from host.domain.com to host2.domain.com
So my idea was to use sed to search for the start of the http until the first / that come after the 2 // thus catching whatever in between and will give me the ability to change both server name port or http\s usage.
Im now using this code (im using echo just for the example):
the example shows how in 2 cases where one time i have a link with http and port 123 converted to https and the second time the other way around
and both code i was using the same sed for generic reasons.
WANTED_URL="https://host.domain.com"
echo 'http://host.domain.com:123/folder1/folder2' | sed -i "s|http.*://[^/]*|$WANTED_URL|"
OR
WANTED_URL="http://host.domain.com:123"
echo 'https://host.domain.com/folder1/folder2' | sed -i "s|http.*://[^/]*|$WANTED_URL|"
is that the correct way doing so?
sed regexes are greedy by default. You can tell sed to consume only non-slashes, like this:
echo 'http://host.domain.com:123/folder1/folder2' | sed -e 's|http://[^/]*|https://host.domain.com|'
result:
https://host.domain.com/folder1/folder2
(BTW you don't have to escape slashes because you are using an alternate separating character)
the key is using [^/]* which will match anything but slashes so it stops matching at the first slash (non-greedy).
You used /.*/ and .* can contain slashes, not that you wanted (greedy by default).
Anyway my approach is different because expression does not include the trailing slash so it is not removed from final output.
Assuming it doesn't really matter if you have 1 sed script or 2 and there isn't a good reason to hard-code the URLs:
$ echo 'http://host.domain.com:123/folder1/folder2' |
sed 's|\(:[^:]*\)[^/]*|s\1|'
https://host.domain.com/folder1/folder2
$ port='123'; echo 'https://host.domain.com/folder1/folder2' |
sed 's|s\(://[^/]*\)|\1:'"$port"'|'
http://host.domain.com:123/folder1/folder2
If that isn't what you need then edit your question to clarify your requirements and in particular explain why:
You want to use hard-coded URLs, and
You need 1 script to do both transformations.
and provide concise, testable sample input and expected output that demonstrates those needs (i.e. cases where the above doesn't work).
wrt what you had:
WANTED_URL="https://host.domain.com"
echo 'http://host.domain.com:123/folder1/folder2' | sed -i "s|http.*://[^/]*|$WANTED_URL|"
The main issues are:
Don't use all-upper-case for non-exported shell variable names to avoid clashes with exported variables and to avoid obfuscating your code (this convention has been around for 40 years so people expect all upper case variables to be exported).
Never enclose any script in double quotes as it exposes the whole script to the shell for interpretation before the command you want to execute even sees it. Instead just open up the single quotes around the smallest script segment possible when necessary, i.e. to expand $y in a script use cmd 'x'"$y"'z' not cmd "x${y}z" because the latter will fail cryptically and dangerously given various input, script text, environment settings and/or the contents of the directory you run it from.
The -i option for sed is to edit a file in-place so you can't use it on an incoming pipe because you can't edit a pipe in-place.
When you let a shell variable expand to become part of a script, you have to take care about the possible characters it contains and how they'll be interpreted by the command given the context the variable expands into. If you let a whole URL expand into the replacement section of a sed script then you have to be careful to first escape any potential backreference characters or script delimiters. See Is it possible to escape regex metacharacters reliably with sed. If you just let the port number expand then you don't have to deal with any of that.

How to escape variables in bash?

I want to do something like this in bash:
echo "Installing WordPress Development..."
noroot wp core install --url=development.local.dev --quiet --title="Local WordPress Dev" --admin_name=admin --admin_email="admin#local.dev" --admin_password="password"
As far as I understand "local" in bash is a reserved word so this code should fail because of this, right? How to escape this local so that it would be interpreted as a string?
There is no need to escape "local". This word has special meaning only when it occurs at the beginning of the line, like local SOME_VAR='some value'. Moreover, it can be used in a function only.
If you try to run the code in your question you will find out that it works and --url=development.local.dev is interpreted as a string without any special meaning.

Remove part of $(hostname)') in bash script?

I am running a script to automatically update a field inside a database with the servers hostname
updatevar="UPDATE email_lists SET owneremail = REPLACE(owneremail, 'replacethis321', '$(hostname)');"
However, the hostname is "xyz.com"; how would I make it only be "xyz"? The script needs to remove the ".com" part. This is used on multiple servers so I can't just set it to xyz.
Instead of using $(hostname) directly, assign it to a shell variable so you can use PE expressions at expansion time:
hostname=$(hostname)
updatevar="... ${hostname%%.*} ..."
The use of %%.* trims everything after the first dot. You could instead use ${hostname%.com} if you only wanted to remove the suffix .com (but leave .org, .net, etc alone).
Alternately, instead of $(hostname), use $(hostname -s) if your operating system supports it; this is the "short" form, truncated at the first dot.
That said -- using string substitutions to form SQL expressions is bad form (and since bash doesn't typically give you a way to use bind variables in running SQL, it's usually necessary/appropriate to use a different language if you genuinely want to do the job right). Even the Wooledge bash guide explicitly calls out database interaction as a limitation.
Alternatively you can pipe to awk instead of getting the hostname in a separate variable.
$(hostname | awk -F "." '{print $1}')

extract as if a key value pair in bash

The other day I stumbled upon a question on SO. If I wanted to extract the value of HOSTNAME in /etc/sysconfig/network which contains
NETWORKING=yes
HOSTNAME=foo
now I can do grep and cut to get the foo but there was some bash magic involved for a similar issue. I don't know what to search for that and I can't seem to find the question now. it involved something like #{HOSTNAME} . As if it was treating HOSTNAME as a key and foo as a value.
If that configuration file is compatible with shell syntax, simply include it as a shell script. IIRC the files in /etc/sysconfig on Red Hat-like distributions are indeed designed to be parsable by a shell. Note that this means that
If shell special characters may end up in a variable's value, they must be properly quoted. For example, var="value with spaces" requires the quotes. var="with\$dollar" requires the backslash.
The script may run arbitrary code that will be executed, so this is only ok if you trust its content.
If these assumptions are valid, then you can go the simple route:
. /etc/sysconfig/network
echo "$HOSTNAME"
Regarding the quoting and braces, see $VAR vs ${VAR} and to quote or not to quote.

Resources