UNIX: Using a regex that matches either single or double quotes with grep - shell

I have a string say
string="MYSTRING"
Now I want to grep for any occurrence of "MYSTRING" (with double quotes) such that there must be parentheses at starting, basically I want to search, in which places "MYSTRING" is used as function's parameter. So,
foo( "MYSTRING" //postive
foo("MYSTRING" //positive
foo('MYSTRING' //positive
foo( 'MYSTRING' //positive
var a = "MYSTRING" //negative
I used:
string="MYSTRING"
regexstring="[(]*\"$string\""
grep -e "$regexString" <<'EOF'
foo( "MYSTRING" //postive
foo("MYSTRING" //positive
foo('MYSTRING' //positive
foo( 'MYSTRING' //positive
var a = "MYSTRING" //negative
EOF
Ideally, all the items with "positive" next to them and none of the items with "negative" will match. What needs to change to make that happen?

The easy way to do this is to use the ksh extension (adopted by bash) $'' to provide a literal string that can include backticks.
#!/usr/bin/env bash
string=MYSTRING
regexstring=$'[(][[:space:]]*[\'"]'"$string"$'[\'"]'
grep -e "$regexstring" "$#"
Breaking down this assignment:
$'[(][[:space:]]*[\'"]'
...is a string literal which evaluates to the following:
[(][[:space:]]*['"]
...thus, it matches a single (, followed by zero or more spaces, followed by either ' or ".
The second part of it is a double-quoted expansion, "$string"; this should be fairly straight on its face.
The final part is $'[\'"']'; just as in the first part, the $'' string-literal syntax is used to generate a string that can contain both ' and " as contents.
By the way -- in POSIX sh, this might instead look like:
regexstring='[(][[:space:]]*['"'"'"]'"$string""['"'"]'
There, we're using single-quoted strings to hold literal double quotes, and double-quoted strings to hold literal single quotes.

Related

Ruby: How to insert three backslashes into a string?

I want to use backticks in ruby for a programm call.
The parameter is a String variable containing one or more backticks, i.e.
"&E?##A`?". The following command yields a new label as its return value:
echo "&E?##A\`?" | nauty-labelg 2>/dev/null
From a ruby program I can call it as follows and get the correct result:
new_label = `echo "&E?##A\\\`?" | nauty-labelg 2>/dev/null`
I want to achieve the same using a variable for the label.
So I have to insert three slashes into my variable label = "&E?##A`?" in order to escape the backtick. The following seems to work, though it is not very elegant:
escaped_label = label.gsub(/`/, '\\\`').gsub(/`/, '\\\`').gsub(/`/, '\\\`')
But the new variable cannot be used in the program call:
new_label = `echo "#{escaped_label}" | nauty-labelg 2>/dev/null`
In this case I do not get an answer from nauty-labelg.
So I have to insert three slashes into my variable label = "&E?##A`?" in order to escape the backtick.
No, you only need to add one backslash for the output. To escape the ` special bash character. The other other two are only for representation proposes, otherwise it isn't valid Ruby code.
new_label = `echo "&E?##A\\\`?" | nauty-labelg 2>/dev/null`
The first backslash will escape the second one (outputting one single backslash). The third backslash escapes the ` character (outputting one single `).
You should only add backslashes before characters that have a special meaning within double quoted bash context. These special characters are: $, `, \ and \n. Those can be escaped with the following code:
def escape_bash_string(string)
string.gsub(/([$`"\\\n])/, '\\\\\1')
end
For label = "&E?##A`?" only the ` should be escaped.
escaped_string = escape_bash_string("&E?##A\`?")
puts escaped_string
# &E?##A\`?

How bash eval expansion work for single qoute double qoute

Someone please help to explain how this work? About the single quote it should not interpret anything but it is not working as what i expected. I expect to get echo $testvar value exactly '"123b"'.
a="testvar"
b="'"123b"'"
eval $a='$b'
echo $testvar
'123b'
a="testvar"
b='"123b"'
eval $a='$b'
echo $testvar
"123b"
a="testvar"
b='"123b"'
eval $a=$b
echo $testvar
123b
Guessing that you want testvar to be <single-quote><double-quote>123b<double-quote><single-quote>:
testvar=\'\"123b\"\'
Consider this in C or Java:
char* str = "123b";
printf("%s\n", str);
String str = "123b";
System.out.println(str);
Why does this write 123b when we clearly used double quotes? Why doesn't it write "123b", with quotes?
The answer is that the quotes are not part of the data. The quotes are used by the programming language to determine where strings start and stop, but they're not in any way part of the string. This is just as true for Bash as for C and Java.
Just like there's no way in Java to differentiate Strings created with "123" + "b" and "123b", there's no way in Bash to tell that b='"123b"' used single quotes in its definition, as opposed to e.g. b=\"123b\".
If given a variable you want to assign its value surrounded by single quotes, you can use e.g.
printf -v testvar "'%s'" "$b"
But this just adds new literal single quotes around a string. It doesn't and cannot care how b was originally quoted, because that information is stored.
To instead add a layer of escaping to a variable, so that when evaluated once it turns into a literal string identical to your input, you can use:
printf -v testvar "%q" "$b"
This will produce a value which is quoted equivalently but not necessarily identically to your original definition. For "value" (a literal with double quotes in it), it may produce \"value\" or '"value"' or '"'value'"' which all evaluate exactly to "value".

Replace " " with "\ " in a file path string with variable expansion

I know there is a better way to do this.
What is the better way?
How do you do a string replace on a string variable in bash?
For Example: (using php because that's what I know)
$path = "path/to/directory/foo bar";
$path = str_replace(" ", "\ ", "$path");
echo $path;
returns:
path/to/directory/foo\ bar
To perform the specific replacement in bash:
path='path/to/directory/foo bar'
echo "${path// /\\ }"
Don't use prefix $ when assigning to variables in bash.
No spaces are allowed around the =.
Note that path is assigned with single quotes, whereas the string replacement occurs in double quotes - this distinction is important: bash does NOT interpret single-quoted strings, whereas you can refer to variables (and do other things) in double-quoted strings; (also, not quoting a variable reference at all has other ramifications, often undesired - in general, double-quote your variable references)
Explanation of string replacement "${path// /\\ }":
In order to perform value substitution on a variable, you start with enclosing the variable name in {...}
// specifies that ALL occurrences of the following search pattern are to be replaced (use / to replace the first occurrence only).
/ separates the search pattern, (a single space), from the replacement string, \\ .
The replacement string, \ , must be represented as \\ , because \ has special meaning as an escape char. and must therefore itself be escaped for literal use.
The above is an instance of what bash (somewhat cryptically) calls shell parameter expansion and also parameter expansion and [parameter and] variable expansion. There are many more flavors, such as for extracting a substring, providing a default value, stripping a prefix or suffix, ... - see the BashGuide page on the topic or the manual.
As for what types of expressions are supported in the search and replacement strings:
The search expression is a globbing pattern of the same type used in filename expansion (e.g, *.txt); for instance, v='dear me'; echo "${v/m*/you}" yields 'dear you'. Note that the longest match will be used.
Additionally, the first character of the pattern has special meaning in this context:
/, as we've seen above, causes all matching occurrences of the pattern to be replaced - by default, only the first one is replaced.
# causes the rest of the pattern to only match at the beginning of the input variable
% only matches at the end
The replacement expression is a string that is subject to shell expansions; while there is no support for backreferences, the fact that the string is expanded allows you to have the replacement string reference other variables, contain commands, with $(...), ...; e.g.:
v='sweet home'; echo "${v/home/$HOME}" yields, for instance, 'sweet /home/jdoe'.
v='It is now %T'; echo "${v/\%T/$(date +%T)}" yields, for instance, It is now 10:05:17.
o1=1 o2=3 v="$o1 + $o2 equals result"; echo "${v/result/$(( $o1 + $o2 ))}" yields '1 + 3 equals 4' (I think)
There are many more features and subtleties - refer to the link above.
How about sed? Is that what you're looking for?
#!/bin/bash
path="path/to/directory/foo bar"
new_path=$(echo "$path" | sed 's/ /\\ /g')
echo "New Path: '$new_path"
But as #n0rd pointed out in his comment, is probably better just quoting the path when you want to use it; something like...
path="path/to/directory/foo bar"
echo "test" > "$path"

How do I store up to 2 names into my variables if needed and also how do I escape a possible single quote in a name?

PhoneBook.txt has names delimited by :
ex= first:last:number - simple
ex= first first:last last:number - 2 firstnames and/or 2 last names
ex= f'irst:l'ast:number - first name or last name with a single quote in it.
My current script
#!/bin/bash
firstName="$1"
lastName="$2"
if [[ "$firstName" == "" ]]; then
read -p "Please enter a first name: " firstName
fi
if [[ "$lastName" == "" ]]; then
read -p "Please enter a last name: " lastName
fi
grep "$firstName:$lastName" PhoneBook.txt
The -F argument tells grep to search for a fixed string instead of a regex.
Use double quotes to delimit your arguments on the command line, e.g.:
./phonebook "first1 first2" "last o'last"
You're free to use single quotes in a double-quoted string.
Just be aware that double-quoted strings are subject to parameter (variable) and command substitution, so instances of literal $ chars. must be escaped as \$.
Similarly, embedded double quotes must be escaped as \".
Note that, by contrast, there is no direct way to embed single quotes in a single-quoted string - you have to break the string apart and insert an escaped single quote, \'; e.g., to single-quote isn't, use 'isn'\''t'; however, you can freely embed double quotes.

Is there any way to break the Bash `quote` function?

Bash in Ubuntu (13.04) seems to have this quote function by default:
quote ()
{
local quoted=${1//\'/\'\\\'\'};
printf "'%s'" "$quoted"
}
This function should always return a correct shell escaped version of it's first parameter.
Is there any input string which will break this function (i.e. return a string unusable for shell input)?
Examples:
quote "A string's but a string."
'A string'\''s but a string.'
quote "A newline *doesn't*
seem to break anything..."
'A newline *doesn'\''t*
seem to break anything...'
It appears to be "bullet-proof".
A singly-quoted item in the shell is absolutely anything between single quotes, other than a single quote. The single quote can be pseudo-included by using the sequence '\'' which terminates one quoting, then backslash-quotes the quote, and then re-starts a new quoting.
This substitution is all that the function does.
The output of the function is single-quote syntax which represents the input string.
The function even defeats the "process-substitution-swallows-trailing-newlines" issue.
Ordinarily, if you have some function or command which outputs multiple lines, followed by one or more blank lines, and then capture it with process substitution like this:
FOO=$(command)
the trailing blank lines are gone. Since quote's output is wrapped in single quotes, trailing newlines in the string are protected, allowing this to work:
FOO=$("a
b
c
")
echo "$FOO"
'a
b
c
'
Clearly, the output of quote is meant to be eval-ed, and quote ensures that the result of this eval is the original argument to quote without any accidental expansion, splitting or stripping of whitespace.

Resources