Why does sed only replace the first character? - sed

$ echo lcdefghijklmnopqrstblvcxyz | tr [a-i] [1-9] | sed 's/j/10/' | sed 's/k/11/' | sed 's/l/12/' | sed 's/m/13/' | sed 's/n/14/' | sed 's/o/15/' | sed 's/p/16/' | sed 's/q/17/' | sed 's/r/18/' | sed 's/s/19/' | sed 's/t/20/' | sed 's/u/21/' | sed 's/v/22/' | sed 's/w/23/' | sed 's/x/24/' | sed 's/y/25/' | sed 's/z/26/'
1234567891011l13141516171819202l223242526
The long command is intended to replace a..z with 1..26. Notice there are 3 "l" characters in the echoed string. Why is the first one correctly converted to "12" yet the other two (results 11l13 and 202l223) aren't?
I tried this on both my Windows 7 PC running Cygwin (bash 4.3.33(1)-release (x86_64-unknown-cygwin)) and on my MacBook Pro running Terminal (bash 3.2) and got the same results. I expected the result to be 1..26 concatenated. This is part of a bigger problem that I reduced to this test case.

You need the g flag for the substitution to be repeated:
$ echo lll | sed 's/l/12/'
12ll
$ echo lll | sed 's/l/12/'g
121212
Without the g flag, s replaces the first instance, as documented in man sed.
Also, you can put all of those commands in a single invocation of sed. You don't need all those pipes:
sed 's/j/10/g;s/k/11/g;s/l/12/g...'

Multiple sed commands (with g switch)
Under bash, you could try something like:
c=1 o=
for i in {a..z};do
o+="s/$i/$((c++))/g;"
done
sed -e "$o" <<<'lcdefghijklmnopqrstblvcxyz'
1234567891011121314151617181920212223242526
or
fold -s <<< ${o//;/; }
s/a/1/g; s/b/2/g; s/c/3/g; s/d/4/g; s/e/5/g; s/f/6/g; s/g/7/g; s/h/8/g;
s/i/9/g; s/j/10/g; s/k/11/g; s/l/12/g; s/m/13/g; s/n/14/g; s/o/15/g; s/p/16/g;
s/q/17/g; s/r/18/g; s/s/19/g; s/t/20/g; s/u/21/g; s/v/22/g; s/w/23/g; s/x/24/g;
s/y/25/g; s/z/26/g;
then
sed -e '
s/a/1/g; s/b/2/g; s/c/3/g; s/d/4/g; s/e/5/g; s/f/6/g; s/g/7/g; s/h/8/g;
s/i/9/g; s/j/10/g; s/k/11/g; s/l/12/g; s/m/13/g; s/n/14/g; s/o/15/g; s/p/16/g;
s/q/17/g; s/r/18/g; s/s/19/g; s/t/20/g; s/u/21/g; s/v/22/g; s/w/23/g; s/x/24/g;
s/y/25/g; s/z/26/g;
' <<<'lcdefghijklmnopqrstblvcxyz'
1234567891011121314151617181920212223242526

This might work for you (GNU sed):
sed -r '1{x;s/^/a1b2c3d4e5f6g7h8i9j10k11l12m13n14o15p16q17r18s19t20u21v22w23x24y25z26/;x};G;:a;s/([a-z])(.*\n.*\1([0-9]+))/\3\2/;ta;P;d' file
This uses a lookup table to translate the required strings.

Related

insert semi colon after 10 digit number

I have lines that start like this: 2141058222 11/22/2017 and I want to append a ; at the end of the ten digit number like this: 2141058222; 11/22/2017.
I've tried sed with sed -i 's/^[0-9]\{10\}\\$/;&/g' which does nothing.
What am I missing?
Try this:
echo "2141058222 11/22/2017" | sed -r 's/^([0-9]{10})/&;/'
echo "2141058222 11/22/2017" | sed 's/ /; /'
Output:
2141058222; 11/22/2017
If the input is always in the format specified, GNU cut works, and might even be more efficient than sed:
cut -c -10,11- --output-delimiter ';' <<< "2141058222 11/22/2017"
Output:
2141058222; 11/22/2017
For an input file that'd be:
cut -c -10,11- --output-delimiter ';' file

How to use Sed command to get following output?

My input is:
"INTC_KEY,ABC1|OBJID,ABC2"
And I want to send the output to a file like:
DDS.INTC_KEY = REPL.OBJID AND DDS.ABC1 = REPL.ABC2
Here is what I've tried so far:
sed 's/^/DDS./g' | sed 's/|/=REPL./g' | tr '\n' '~' | sed 's/~/_N~/g' | sed 's/~$/\n/g' | sed 's/~/~\n/g' | sed 's/~/ AND/g' > ${LOG_DIR}/JOIN.tmp
Based on the single line of input, the following regular expression will transform the input into the desired output:
s/"\([^,]*\),\([^|]*\)|\([^,]*\),\(.*\)"/DDS.\1 = REPL.\3 AND DDS.\2 = REPL.\4/
This shell command shows it working:
$ echo '"INTC_KEY,ABC1|OBJID,ABC2"' | sed 's/"\([^,]*\),\([^|]*\)|\([^,]*\),\(.*\)"/DDS.\1 = REPL.\3 AND DDS.\2 = REPL.\4/'
DDS.INTC_KEY = REPL.OBJID AND DDS.ABC1 = REPL.ABC2
The regular expression basically matches four pieces of text (using the escaped parentheses), delimited by the commas and vertical bar and made available as the \1-\4 back references for the substitution.
Note: I’ve tried to stick to using the features of standard sed and I tested using GNU sed with the POSIXLY_CORRECT environment variable set to 1 to emulate standard sed.

Print pattern on a string with special character

How to print only string figure with the following line :
\begin{figure}[h!]
I tried :
firstLine='\begin{figure}[h!]'
echo $firstLine | sed -n 's/\\begin{\(.*\)}/\1/p'
but returns :
figure[h!] instead of figure
It seems that issue comes from [] or ! character.
firstLine='\begin{figure}[h!]'
echo "$firstLine" | sed 's/.*{\(.*\)}.*/\1/'
Output:
figure
With your code (add .*):
echo $firstLine | sed -n 's/\\begin{\(.*\)}.*/\1/p'
This might work for you (GNU sed):
sed 's/.*{\(.*\)}.*/\1/' file
This assumes there is only one {...} expression and one line.
A more rigorous solution would be:
sed -n 's/.*\\begin{\([^}]*\)}.*/\1/p' file
However nothing would be output if no match was found.

Unix - Removing everything after a pattern using sed

I have a file which looks like below:
memory=500G
brand=HP
color=black
battery=5 hours
For every line, I want to remove everything after = and also the =.
Eventually, I want to get something like:
memory:brand:color:battery:
(All on one line with colons after every word)
Is there a one-line sed command that I can use?
sed -e ':a;N;$!ba;s/=.\+\n\?/:/mg' /my/file
Adapted from this fine answer.
To be frank, however, I'd find something like this more readable:
cut -d = -f 1 /my/file | tr \\n :
Here's one way using GNU awk:
awk -F= '{ printf "%s:", $1 } END { printf "\n" }' file.txt
Result:
memory:brand:color:battery:
If you don't want a colon after the last word, you can use GNU sed like this:
sed -n 's/=.*//; H; $ { g; s/\n//; s/\n/:/g; p }' file.txt
Result:
memory:brand:color:battery
This might work for you (GNU sed):
sed -i ':a;$!N;s/=[^\n]*\n\?/:/;ta' file
perl -F= -ane '{print $F[0].":"}' your_file
tested below:
> cat temp
abc=def,100,200,dasdas
dasd=dsfsf,2312,123,
adasa=sdffs,1312,1231212,adsdasdasd
qeweqw=das,13123,13,asdadasds
dsadsaa=asdd,12312,123
> perl -F= -ane '{print $F[0].":"}' temp
abc:dasd:adasa:qeweqw:dsadsaa:
My command is
First step:
sed 's/([a-z]+)(\=.*)/\1:/g' Filename |cat >a
cat a
memory:
brand:
color:
battery:
Second step:
sed -e 'N;s/\n//' a | sed -e 'N;s/\n//'
My output is
memory:brand:color:battery:

Can sed search & replace on a match if that match in only part of a line?

The sed below will output the input exactly. What I'd like to do is replace all occurrences of _ with - in the first matching group (\1), but not in the second. Is this possible?
echo 'abc_foo_bar=one_two_three' | sed 's/\([^=]*\)\(=.*\)/\1\2/'
abc_foo_bar=one_two_three
So, the output I'm hoping for is:
abc-foo-bar=one_two_three
I'd prefer not to resort to awk since I'm doing a string of other sed commands too, but I'll resort to that if I have to.
Edit: Minor fix to RE
You can do this in sed using the hold space:
$ echo 'abc_foo_bar=one_two_three' | sed 'h; s/[^=]*//; x; s/=.*//; s/_/-/g; G; s/\n//g'
abc-foo-bar=one_two_three
You could use awk instead of sed as follows:
echo 'abc_foo_bar=one_two_three' | awk -F= -vOFS== '{gsub("_", "-", $1); print $1, $2}'
The output would be, as expected:
abc-foo-bar=one_two_three
You could use ghc instead of sed as follows:
echo "abc_foo_bar=one_two_three" | ghc -e "getLine >>= putStrLn . uncurry (++) . (map (\x -> if x == '_' then '-' else x) *** id) . break (== '=')"
The output would be, as expected:
abc-foo-bar=one_two_three
This might work for you:
echo 'abc_foo_bar=one_two_three' |
sed 's/^/\n/;:a;s/\n\([^_=]*\)_/\1-\n/;ta;s/\n//'
abc-foo-bar=one_two_three
Or this:
echo 'abc_foo_bar=one_two_three' |
sed 'h;s/=.*//;y/_/-/;G;s/\n.*=/=/'
abc-foo-bar=one_two_three