Path substitution with sed and shell variables on OS X - shell

I am on Mac OS X and using sed for an in-place replacement.
Essentially I have this:
#!/bin/sh -e
PREFIX="$1"
sed -i bak -e 's|OCAMLDIR|"${PREFIX}"|g' ocamloptrev
Where PREFIX is a path, hence I'm using the |.
Unfortunately, the variable in the file path is not getting evaluated as I expected, I end up with:
OCAMLC="${PREFIX}"/bin/ocamlopt
How can I get the right evaluation of ${PREFIX} into the sed command?

Try this:
#!/bin/sh -e
PREFIX="$1"
sed -i bak -e 's|OCAMLDIR|'"${PREFIX}"'|g' ocamloptrev
What you're basically doing, is "exiting"/getting outside the single-quoted string, entering into a double-quoted string, interpreting the variable inside double-quotes, and then entering the single quotes again.
With this simple example, we could also just use double-quotes, which allow variables to be interpreted:
#!/bin/sh -e
PREFIX="$1"
sed -i bak -e "s|OCAMLDIR|${PREFIX}|g" ocamloptrev
If you try to use double-quotes ("") inside single-quotes, they don't get interpreted either. This part of the Bash manual explains this in more detail.
3.1.2.2 Single Quotes
Enclosing characters in single quotes (‘'’) preserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash.
3.1.2.3 Double Quotes
Enclosing characters in double quotes (‘"’) preserves the literal value of all characters within the quotes, with the exception of $, `, \, and, when history expansion is enabled, !. The characters $ and ` retain their special meaning within double quotes (see Shell Expansions). ...

Shell variables are not expanded inside single quotes (there are no metacharacters within single quotes, not even backslashes), so you need to use something like this, with the double quotes around ${PREFIX} ensuring that spaces etc in the value are handled correctly:
sed -i bak -e 's|OCAMLDIR|'"${PREFIX}"'|g' ocamloptrev
Or you could even use:
sed -i bak -e "s|OCAMLDIR|${PREFIX}|g" ocamloptrev
The latter is safe because the material inside the double quotes does not contain shell metacharacters (dollar signs, backslashes and back-quotes are the main danger signs). If there were dodgy characters in the rest of the string, the first version is safer to use.
Personally, I'd use .bak rather than just bak as the suffix.

Related

How do I escape a single quote inside single quotes in jenkins-pipeline bash command

in jenkins-pipeline, I'm trying to use SED to append the following line to the end of a file.
sh "sed -i '\$ s/\$/ public_file=\\/var\\/lib\\/jenkins\\/workspace\\/test-project\\ ansible_ssh_common_args='-o StrictHostKeyChecking=no' /' file.txt"
but I can't figure out how to escape the below line in my Jenkins file to make it work.
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
I've already tried the following, which works in katacoda playground, but not in jenkins pipeline.
'"'"'-o StrictHostKeyChecking=no'"'"' /' file.txt
use " to wrap sed command, then you can use ' in command directly without to escape it.
use #, but / as delimiter for sed s command, then you no need to escape the / appeared in file path to make the whole commend more concise and readable.
sed -i "\$ s#\$# public_file=/var/lib/jenkins/workspace/test-project ansible_ssh_common_args='-o StrictHostKeyChecking=no' #" file.txt
In bash, you cannot escape single quotes within single quotes.
See the bash manual page:
Enclosing characters in single quotes preserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash.
You can use double quotes instead; then escape them as needed. You can then use single quotes within them without further escaping.

Terminal: How do I pass arguments into an alias?

In a directory which is added to my $PATH I have the following file:
filename="$1" day="$2" month="$3"
ruby -r "./SomeClass.rb" -e 'SomeClass.run($filename, $day, $month)'
Suppose this file is called someclass. When I type someclass into terminal my system recognizes it as a valid command and runs the corresponding Ruby file correctly. But the arguments are not being passed in. How do I pass arguments into an alias?
How do I pass arguments into an alias?
Don't use an alias, better to declare a function for this. You can pass arguments and do other stuff easily inside a bash function:
someclass() {
# make sure enough arguments are passed in
# echo "$1 - $2 - $3";
filename="$1"; day="$2"; month="$3";
ruby -r "./SomeClass.rb" -e "SomeClass.run($filename, $day, $month)";
}
You are using single quote. Change them to double:
ruby -r "./SomeClass.rb" -e "SomeClass.run($filename, $day, $month)"
No expansion are done within single quotes. From bash manual:
3.1.2.2 Single Quotes
Enclosing characters in single quotes (‘'’) preserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash.
3.1.2.3 Double Quotes
Enclosing characters in double quotes (‘"’) preserves the literal value of all characters within the quotes, with the exception of ‘$’, ‘`’, ‘\’, and, when history expansion is enabled, ‘!’. The characters ‘$’ and ‘`’ retain their special meaning within double quotes (see Shell Expansions). The backslash retains its special meaning only when followed by one of the following characters: ‘$’, ‘`’, ‘"’, ‘\’, or newline. Within double quotes, backslashes that are followed by one of these characters are removed. Backslashes preceding characters without a special meaning are left unmodified. A double quote may be quoted within double quotes by preceding it with a backslash. If enabled, history expansion will be performed unless an ‘!’ appearing in double quotes is escaped using a backslash. The backslash preceding the ‘!’ is not removed.
A useful page on quotes I am using is on grymoire.
Regarding the title of the question, check this question (Alias with variable in bash) and its answer: you can't and have to use functions.

Must special characters in bash double quoted or escaped with \

Should I double quote or escape with \ special characters like ',
$ echo "'"
'
$ echo \'
'
Here is apparently doesn't matter, but are there situations where there is a difference, except for $, `` or`, when I know there is a difference.
Thanks,
Eric J.
You can use either backslashes, single quotes, or (on occasion) double quotes.
Single quotes suppress the replacement of environment variables, and all special character expansions. However, a single quote character cannot be inside single quotes -- even when preceded by a backslash. You can include double quotes:
$ echo -e 'The variable is called "$FOO".'
The variable is called "$FOO".
Double quotes hide the glob expansion characters from the shell (* and ?), but it will interpolate shell variables. If you use echo -e or set shopt -s xpg_echo, the double quotes will allow the interpolation of backslash-escaped character sequences such as \a, and \t. To escape those, you have to backslash-escape the backslash:
$ echo -e "The \\t character sequence represents a tab character."
The \t character sequence represents a tab character."
The backslash character will prevent the expansion of special characters including double quotes and the $ sign:
$ echo -e "The variable is called \"\$FOO\"."
The variable is called "$FOO".
So, which one to choose? Which everyone looks the best. For example, in the preceding echo command, I would have been better off using single quotes and that way I wouldn't have the confusing array of backslashes one right after another.
On the other hand:
$ echo -e "The value of \$FOO is '$FOO'."
The value of FOO is 'bar'.
is probably better than trying something like this:
$ echo -e 'The value of $FOO is '"'$FOO'."
Readability should be the key.

Expand variables in sed

I need use sed into bash script, for add lines after any line numer of script with some pair of values (below work)
sed -i.bak '14i\some_text=some_text' file
But I need on script bash (sh) for expand variables (below not work)
sed -i.bak '$number_linei\$var1=$var2' $var3
Just use double quotes instead of single quotes. You'll also need to use {} to delimit the number_line variable correctly and escape the \, too.
sed -i.bak "${number_line}i\\$var1=$var2" $var3
I'd personally prefer to see all of the variables use the {}, ending up with something like:
sed -i.bak "${number_line}i\\${var1}=${var2}" ${var3}
Change single quotes to double quotes:
man bash:
Enclosing characters in single quotes preserves the literal value of
each character within the quotes.
Enclosing characters in double quotes preserves the literal value of
all characters within the quotes, with the exception of $, `, \, and,
when history expansion is enabled, !. The characters $ and ` retain
their special meaning within double quotes.

How to match a single quote in sed

How to match a single quote in sed if the expression is enclosed in single quotes:
sed -e '...'
For example need to match this text:
'foo'
You can either use:
"texta'textb" (APOSTROPHE inside QUOTATION MARKs)
or
'texta'\''textb' (APOSTROPHE text APOSTROPHE, then REVERSE SOLIDUS, APOSTROPHE, then APOSTROPHE more text APOSTROPHE)
I used unicode character names. REVERSE SOLIDUS is more commonly known as backslash.
In the latter case, you close your apostrophe, then shell-quote your apostrophe with a backslash, then open another apostrophe for the rest of the text.
As noted in the comments to the question, it's not really about sed, but how to include a quote in a quoted string in a shell (e.g. bash).
To clarify a previous answer, you need to escape the quote with a backslash, but you can't do that within a single-quoted expression. From the bash man page:
Enclosing characters in single quotes
preserves the literal value of each
character within the quotes. A single
quote may not occur between single
quotes, even when preceded by a
backslash.
Therefore, you need to terminate the quoted expression, insert the escaped quote, and start a new quoted expression. The shell's quote removal does not add any extra spaces, so in effect you get string concatenation.
So, to answer the original question of how to single quote the expression 'foo', you would do something like this:
sed -e '...'\''foo'\''...'
(where '...' is the rest of the sed expression).
Overall, for the sake of readability, you'd be much better off changing the surrounding quotes to double quotes if at all possible:
sed -e "...'foo'..."
[As an example of the potential maintenance nightmare of the first (single quote) approach, note how StackOverflow's syntax highlighting colours the quotes, backslashes and other text -- it's definitely not correct.]
For sed, a very simple solution is to change the single quotation format to a double quote.
For a given variable that contains single quotes
var="I'm a string with a single quote"
If double quotes are used for sed, this will match the single quote.
echo $var | sed "s/'//g"
Im a string with a single quote
Rather than single quotes, which will hang
echo $var | sed 's/'//g'
You can also use ['] to match a literal single quote without needing to do any shell quoting tricks.
myvar="stupid computers can't reason about life"
echo "$myvar" | sed -e "s/[']t//"
Outputs:
stupid computers can reason about life

Resources