Exclude matched character from output - shell

I have a script shell with one parameter.
./script.sh 121-0/2/3
I want to print only after the "-":
Output :
0/2/3
how to do this in shell ??

Look for the $ { variable # pattern }
If the pattern matches the beginning of the variable's value, delete the shortest part that matches and return the rest.
In your case:
var = $1 #(command line argument)
res = ${var # *-} #Wrong: spaces
res = ${var#*-} #gives your response
For instance you can look up it here

Related

Replace Last Occurrence of Substring in String (bash)

From the bash software manual:
${parameter/pattern/string}
The pattern is expanded to produce a
pattern just as in filename expansion. Parameter is expanded and the
longest match of pattern against its value is replaced with string.
... If pattern begins with ‘%’, it must match
at the end of the expanded value of parameter.
And so I've tried:
local new_name=${file/%old/new}
Where string is an absolute file path (/abc/defg/hij and old and new are variable strings.
However this seems to be trying to match the literal %sb1.
What is the syntax for this?
Expected Output:
Given
old=sb1
new=sb2
Then
/foo/sb1/foo/bar/sb1 should become /foo/sb1/foo/bar/sb2
/foo/foosb1other/foo/bar/foosb1bar should become /foo/foosb1other/foo/bar/foosb2bar
Using only shell-builtin parameter expansion:
src=sb1; dest=sb2
old=/foo/foosb1other/foo/bar/foosb1bar
if [[ $old = *"$src"* ]]; then
prefix=${old%"$src"*} # Extract content before the last instance
suffix=${old#"$prefix"} # Extract content *after* our prefix
new=${prefix}${suffix/"$src"/"$dest"} # Append unmodified prefix w/ suffix w/ replacement
else
new=$old
fi
declare -p new >&2
...properly emits:
declare -- new="/foo/foosb1other/foo/bar/foosb2bar"

BASH - Capture string between a FIXED and 2 possible variables

To get what is between "aa=" and either % or empty
string = "aa=value%bb"
string2 = "bb=%aa=value"
The rule must work on both strings to get the value of "aa="
I would like a BASH LANGUAGE solution if possible.
Use this:
result=$(echo "$string" | grep -o 'aa=[^%]*')
result=${result:3} # remove aa=
[^%]* matches any sequence of characters that doesn't contain %, so it will stop when it gets to % or the end of the string. $(result:3} expands to the substring starting from character 3, which removes aa= from the beginning.

sed match the 2nd occurrence of the pattern

Updating the question:
I have a file that contains block of code in one of the two formats mentioned below:
1.
$Subj .= "HAD PROBLEMS";
if ($To) {
$Cc = "abc\#example.com";
}
else {
$Cc = "abc\#example.com";
}
2.
$Subj .= "HAD PROBLEMS";
if ($To) { $Cc = "abc\#example.com"; } else { $Cc = "abc\#example.com"; }
I need to replace the email id in $Cc variable(2nd occurrences) with a new email id for both the formats.
I have the following sed command to do so.
sed '\|HAD PROBLEMS|,/}/ s/$Cc = \(\"[A-Za-z0-9]*\)\(.*\)\([A-Za-z0-9]*\)\#example.com\"/\$Cc = "new email\\#example.com"/' test.txt
This command will replace the email id only for the $Cc variable in the if {} block (first occurrence of $Cc) since my ending match pattern is a '}'. I want to replace email id in the second $Cc as well. how do i match on the 2nd occurrence of '}' ?
The solution mentioned below works for the first format. But I need a generic solution that works for both the formats. Can anyone please help.
I hope the description is clear.
Seems to me that awk would be a clearer language in which to express this problem.
awk '/HAD PROBLEMS/{x=1} /}/{x++} x==2&&/\$Cc =/{sub(/[a-z]+\\#[a-z.-]+/,"new#example.com"); unset x} 1' input.txt
Or, spaced out for easier commenting:
# search for your start pattern, set a counter.
/HAD PROBLEMS/ {x=1}
# increment the counter at the first close-squigly.
/}/ {x++}
# If we've done our increment and we're on the right line,
x==2 && /\$Cc =/ {
# substitute the existing address with a new one,
sub(/[a-z]+\\#[a-z.-]+/,"new#example.com");
# and reset our counter.
unset x
}
# Print the current line.
1

How do i split string contain parentheses into array and remain the substring in parentheses in shell script

I have a string like this:
a1="a,b,c,(d,e),(f,g)";
How to get the array like
arr=["a","b","c","d,e","f,g"];
I want to replace the comma between parentheses with some other character and revert it after having converted into array
But i do not know how to replace only the comma between parentheses;
how can this be done?
GNU sed parser
sed 's/,/\",\"/g;s/(\(.\)\"/\1/g;s/\"\(.\))/\1/g;s/^\w\+=\"/arr=[\"/;s/;/];/'
Try following bash script where I parse the string using regular expression. It's awkward for me but seems to work:
#!/usr/bin/env bash
unset arr
a1="a,b,c,xxx(d,e),sdf(f,g)"
## The regular expression does an alternation between
## a pair of parens followed by an optional comma "\([^\)]+\)(,?)"
## or any characters followed by a comma or end of line "[^,]+(,|$)"
## After that I save all the rest of the string to match it in
## following iterations.
while [[ $a1 =~ ([^\(,]*\([^\)]+\)(,?)|[^,]+(,|$))(.*) ]]; do
## BASH_REMATCH keeps grouped expressions. The first one
## has the data extracted between commas. This removes the
## trailing one.
elem="${BASH_REMATCH[1]%,}"
## Remove opening paren, if exists one.
elem="${elem/\(/}"
## Remove trailing paren, if exists one.
elem="${elem%)}"
## Add element to an array.
arr+=("$elem")
## Use the string left (fourth grouped expression in
## the regex) to continue matching elements.
a1="${BASH_REMATCH[4]}"
done
printf "%s\n" "${arr[#]}"
Running it like:
bash script.sh
It yields:
a
b
c
xxxd,e
sdff,g
Write a parser! :D
I have no idea how to do this in bash, but I can show you how to do it in PHP (should be transferable to other languages).
$str = "a,b,c,(d,e),(f,g)";
$out = array();
$current_token = "";
$open_brackets = 0;
$length = strlen($str)
for ($i = 0; $i < $length; $i += 1) {
$chr = $str[$i];
if ($chr === "(") {
$open_brackets += 1;
} else if ($chr === ")") {
$open_brackets -= 1;
} else if ($open_brackets === 0 && $chr === ",") {
$out[] = $current_token; // push token value to out
$current_token = "";
} else {
$current_token .= $chr;
}
}
if (strlen($current_token) > 0) {
$out[] = $current_token; // dont forget the last one
}
var_dump($out); // ["a","b","c","d,e","f,g"]
Untested, but this is the outline. Keep track of number of brackets and only when the brackets are matched should , be interpretted as a delimiter.

Replace or append block of text in file with contest of another file

I have two files:
super.conf
someconfig=23;
second line;
#blockbegin
dynamicconfig=12
dynamicconfig2=1323
#blockend
otherconfig=12;
input.conf
newdynamicconfig=12;
anothernewline=1234;
I want to run a script and have input.conf replace the contents between the #blockbegin and #blockend lines.
I already have this:
sed -i -ne '/^#blockbegin/ {p; r input.conf' -e ':a; n; /#blockend/ {p; b}; ba}; p' super.conf
It works well but until I change or remove #blockend line in super.conf, then script replaces all lines after #blockbegin.
In addition, I want script to replace block or if block doesn't exists in super.conf append new block with content of input.conf to super.conf.
It can be accomplished by remove + append, but how to remove block using sed or other unix command?
Though I gotta question the utility of this scheme -- I tend to favor systems that complain loudly when expectations aren't met instead of being more loosey-goosey like this -- I believe the following script will do what you want.
Theory of operation: It reads in everything up-front, and then emits its output all in one fell swoop.
Assuming you name the file injector, call it like injector input.conf super.conf.
#!/usr/bin/env awk -f
#
# Expects to be called with two files. First is the content to inject,
# second is the file to inject into.
FNR == 1 {
# This switches from "read replacement content" to "read template"
# at the boundary between reading the first and second files. This
# will of course do something suprising if you pass more than two
# files.
readReplacement = !readReplacement;
}
# Read a line of replacement content.
readReplacement {
rCount++;
replacement[rCount] = $0;
next;
}
# Read a line of template content.
{
tCount++;
template[tCount] = $0;
}
# Note the beginning of the replacement area.
/^#blockbegin$/ {
beginAt = tCount;
}
# Note the end of the replacement area.
/^#blockend$/ {
endAt = tCount;
}
# Finished reading everything. Process it all.
END {
if (beginAt && endAt) {
# Both beginning and ending markers were found; replace what's
# in the middle of them.
emitTemplate(1, beginAt);
emitReplacement();
emitTemplate(endAt, tCount);
} else {
# Didn't find both markers; just append.
emitTemplate(1, tCount);
emitReplacement();
}
}
# Emit the indicated portion of the template to stdout.
function emitTemplate(from, to) {
for (i = from; i <= to; i++) {
print template[i];
}
}
# Emit the replacement text to stdout.
function emitReplacement() {
for (i = 1; i <= rCount; i++) {
print replacement[i];
}
}
I've written perl one-liner:
perl -0777lni -e 'BEGIN{open(F,pop(#ARGV))||die;$b="#blockbegin";$e="#blockend";local $/;$d=<F>;close(F);}s|\n$b(.*)$e\n||s;print;print "\n$b\n",$d,"\n$e\n" if eof;' edited.file input.file
Arguments:
edited.file - path to updated file
input.file - path to file with new content of block
Script first delete block (if find one matching) and next append new block with new content.
You mean say
sed '/^#blockbegin/,/#blockend/d' super.conf

Resources