Is it possible to do simple arithmetic in sed addresses? - linux

Is it possible to do simple arithmetic in sed addresses?
Judging by the "addresses" manual section, the answer seems no. But maybe there is a workaround?
For example, how can I print the second last line of a file? It would be cool something like:
sed -n '$-1 p' file
But it obviously does not work... so I usually have to do multiple sed calls, first for identifying the line, then do the arithmetic using the shell $((expr)) and then finally call sed again. Like this:
sed -n "$(($(sed -n '$ =' file)-1)) p" file
Is there a "better", more compact, more readable way for doing arithmetics with sed addresses?
In a serious moment of procrastination, I decided to write a small script that quickly changes the xterm colorscheme. The idea is that you have the .Xresources a file with a start marker and an end marker:
...
START_MARKER
...
END_MARKER
...
and you want to delete everything that is between the markers, but not the markers themselves. Again, it would be great to do something like:
sed '/START_MARKER/+1,/END_MARKER/-1 d' file
...but you can't!

You're right, one can't directly do math in sed1, even addresses. But you can use some trickery to do what you want:
Second-last row:
$ seq 5 | sed -n -e '${ # On the last line
> g # Replace the buffer with the hold space
> p # and print it
> }
> h' # All lines, store the current line in the hold space.
4
Between START and END:
$ cat test.in
1
START
2
3
END
4
$ cat test.in | sed '/^START$/,/^END$/{
> /^START$/d
> /^END$/d
> p
> }
> d'
2
3
$ cat test.in | sed -n -e '/^START$/,/^END$/!d' -e '/^START/d' -e '/^END$/d' -e p
2
3
I'm using a BSD (mac) sed; on GNU systems you can use ; between lines instead of a newline. Or stick it in a script.
1: Sed is Turing complete, so you can do math, but it's unwieldy at best: http://rosettacode.org/wiki/A%2BB#sed
Yes, I know, UUOC; it's for illustration only

Delete the second last line:
sed ':r;$!{N;br};s/\n[^\n]*\(\n[^\n]*\)$/\1/' file
Delete everything inside markers:
sed ':r;$!{N;br};s/START_MARKER.*END_MARKER/START_MARKER\nEND_MARKER/' file
Far from being elegant, but kinda works.
As it was mentioned in the comments, sed operates on lines. However, you can read another line into the pattern space with N command. The two lines will now both be in the pattern space and will be separated with a \n. sed also has means of execution flow control, namely labels and conditional/unconditional branches. Everything is documented in man sed, also here is a full reference with examples. In the code above r is a label; $!{..} means "everywhere except last line, do ..; N;br reads another line and branches unconditionally to r again. So with :r;$!{N;br} you read all the input into the pattern space and then you operate on it as a single line with \n separating lines of the input.

This might work for you (GNU sed);
sed '$!N;$s/.*\n//;P;D' file
and this works and should be easy to understand:
sed '/start/,/end/!d;//d' file
These are solutions to your questions but as for arithmetic best use awk or perl.

You have some good sed suggestions, here's one based on GNU awk:
awk -v RS='START_MARKER|END_MARKER' 'RT == "END_MARKER"' infile
RS='START_MARKER|END_MARKER' splits input with the markers as separators.
RT is set to the matched separator, when it matches "END" the default block {print $0} is executed.
So for example if you wanted to print all but the last three lines, set FS to \n and apply the appropriate loop:
awk -v RS='START_MARKER|END_MARKER' -v FS='\n' 'RT == "END" { for(i=1; i<NF-3; i++) print $i }' infile

You can use simple method to show second last line of the file.
TOTAL_LENGTH=$(cat file_name | wc -l)
SECOND_LAST_LINE=`expr $TOTAL_LENGTH - 1`
head -$SECOND_LAST_LINE | tail -1
If you want to delete the second last line from the file:
sed -i "$SECOND_LAST_LINE"d file_name

A more comprehensive treatment for doing arithmetic in sedis given in solution #2. An introduction to using sed to `sed' its own script is here.
As the brain pain strain incurred in solution #2 from the quixotic comment demands of too much "hand waving" actually is too much "hand waving" of code, in juxtaposition, this is solution #3:
echo -e 'a\nb\nc\nd\ne' | sed -n '1!G;h;$p' | sed -n 3p
which still uses piping ("But maybe there is a workaround?"), where the numeral 3 must be replaced "by hand" for the desired line from the end of the file ala $-3.
Suppose the sed script is '$-4 p; $-6p; $-8 p;'
echo -e 'a\nb\nc\nd\ne\nf\ng\nh\ni' |
sed -n '1!G;h;$p' |
sed -n '4 p; 6p; 8 p;' |
sed -n '1!G;h;$p'
does the job via
echo '$-4 p; $-6p; $-8 p;' | sed s/$-//
Caveats:
The sed commands must be as simple as print.
The "simple arithmetic" can only be of the form '$-n'.
The arithmetic is not calculated "normally".
A "single" 'sed' command string (a "line" if the previous piping is considered as such) would embed and combine these two commands as outlined in the next answer #2.
The coup de grĂ¢ce.
Given the perfunctory dismissal of the first answer here is #2:
As this is only the 2nd or 3rd time writing a substantial sed script, serious syntax subtlety (s)circumvention scuppering solutions seemed sufficient: ala
# file prep
echo -e ' a\n b\n c\n d\n e\n f' >test
The following strikeout is not incorrect but after playing and "messing about" with sed with an SO problem over here the sed execute can be simpler w/o IO redirection if run from the pattern buffer to get the file length line count $ via:
sed -e '1{h; s/.*/sed -n "$=" test /e' -e 'p;x}; ${p;x;}' test
The $= enumeration is held in the hold buffer from the get go and printed again at the end.
# get "sed -n $= test" command output into sed script
sed -n '1esed -n "$=" test >sedr' test
# see where this is headed? so far "sed -n ... test" is irrelevant
# a pedantic "sed" only solution would keep it this way with
# all the required "sed"'ng as part of an 'e' command or '$e'
# where the 'sedr' file is itself "sed"'d ultimately to a final
# command 'sed -n /<the calculated line number>/p'
# one could quibble whether '>sedr' io redirection is "pure sed"
# modify 'sedr'with [the sed RPN][1] to get <the calculated line number>
# with judicious use of "sed"'s 'r' command and buffering will
# realize the effective script to compute the desired result
# this is left as an exercise needing perverse persistence with
# a certain amount of masochistic agony
As a hint as to how to proceed; using the technique of solution #3 the sed script $- addresses are now replaced by the $= value and -. So sed is again used to edit its own script.
Parsing the sed script must accurately modify just the $- in addresses only.
Also, to use the RPN calculator the infix arithmetic must have post fixed operators. It is a conventional paradigm in theories of automata and formal languages to convert Polish Notation or its Reverse to Infix and vice versa.
Hopefully, this establishes the answer in the afirmative that it can be done (mais, pas par moi) and in the negative that it is not a trivial exercice (c'est par moi).
Excruciating rationale for an arbitrary solution is at the end.
The environment used for the empirical tests:
linuxuser#ubuntu:~$ sed --version
sed (GNU sed) 4.4
Copyright (C) 2017 Free Software Foundation, Inc.
linuxuser#ubuntu:~$ uname -a
Linux ubuntu 4.15.0-74-generic #84-Ubuntu SMP Thu Dec 19 08:06:00 UTC 2019 i686 i686 i686 GNU/Linux
linuxuser#ubuntu:~$ lsbname -a
lsbname: command not found
linuxuser#ubuntu:~$ apropos lsb
lsb_release (1) - print distribution-specific information
lsblk (8) - list block devices
linuxuser#ubuntu:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.2 LTS
Release: 18.04
Codename: bionic
Solution #1
A technique thinking outside inside the box:
seq 60 | sed -n '$!p' | sed -n '$!p' | sed -n '$!p' | sed -n '$p'
which prints:
57
specifically, for the second last line:
sed -n '$!p' file | sed -n '$p'
More generally, a script can iterate over sed -n '$!p' to "count backwards" from the end of a file.
Well, the answer to:
Is it possible to do simple arithmetic in sed addresses?
rhetorically is, it depends on one's abilities, wishes and desires as well as a realistic assessment of practicality. As well, the implication is that a single sed invocation should be used for this task exclusively. But yes it is possible.
A firm grounding in the studies of Automata, Formal Languages and Recursive Function Theory does not hurt.
As stated in previous answers: Not only can sed do simple arithmetic but also any computable function which includes complex arithmetic. To do so however requires implementing the Primitive Recursive Functions (PRF) (which of course sed does) of Recursive Function Theory (RFT). Of course the finite size of machine architecture does limit the computation without infinite tape resources as a Turing machine proves. In any case not wishing to demonstrate this the precedents are to be found in the sed manual.
Specifically, to do arithmetic (finitely) an RPN calculator:
https://www.gnu.org/software/sed/manual/html_node/Increment-a-number.html#FOOT9
Now then, using such a tool can create a sed script that precomputes the arithmetic that is then embedded in a sed script to print the desired output. A simple demonstration is given by the OP noting now that the shell arithmetic computation can be done using the RPN sed script.
This reduces to a form such as (very crude)
sed '/$(sed RPN($= - 3*4) file)/;p;' file
but still requires feeding sed a sed'd script.
Also, there is arguably the quibbling over the use of bash $() but it could be argued bash is already used to execute the first 'sed' so no harm no foul.
Recognizing that sed implements the PRF or equivalently is Turing complete means that yes, a single invocation of sed is adequate.
The paradigm can therefore do this.
Some commands that could expedite this task are:
e, e command, r, R, w, W
in addition to the usual hold and pattern buffer commands.
The r, R, w, W commands are particularly advantageous as temporary buffer space.
e [command] [3.7 Commands Specific to GNU sed][2]
This command allows one to pipe input from a shell command into
pattern space. Without parameters, the e command executes the
command that is found in pattern space ...
More abstractly, it is completely possible, though highly impractical, to write a sed script to execute the sed paradigm itself that also includes arithmetic calculations even in addresses.
A sed peculiarity. The expression /\n/ will not match any address and matches in pattern space only if a sed command like 'N'ext or s/.*/\n/ introduces one.
Confirmed via:
echo -e '\n\n' | sed -n ' /\n/ {s//hello/;p}'
But
echo -e '\n\n' | sed -n '0,/\n\n\n/ {s//hello/;p}'
outputs 3 blank lines and
echo -e '\n\n' | sed -n '0,/\n/ {s/.*/hello/;p}'
echo -e '\n\n' | sed -n '0,/\n\n\n/ {s/.*/hello/;p}'
each output 3 hello's
hello
hello
hello
while this is well-behaved:
echo -e '\n\n' | sed -n '0,/^$/ {s//hello/;p}'

Related

Insert line number in a file

Would like to insert line number at specific location in file
e.g.
apple
ball
should be
(1) apple
(2) ball
Using command
sed '/./=' <FileName>| sed '/./N; s/\n/ /'
It generates
1 Apple
2 Ball
1st solution: This should be an easy task for awk.
awk '{print "("FNR") "$0}' Input_file
2nd solution: With pure sed as per OP's attempt try:
sed '=' Input_file | sed 'N; s/^/(/;s/\n/) /'
Easy to do with perl instead:
perl -ne 'print "($.) $_"' foo.txt
If you want to modify the file in-place instead of just printing out the numbered lines on standard output:
perl -ni -e 'print "($.) $_"' foo.txt
Many ways are there to insert line numbers in a file
some of them are :-
1.Using cat command
cat -n file.txt > newfile.txt
2.Using nl command
nl -b a file.txt
Awk and perl both are very usefull and powerfull. But if, like me, you are reluctant to learn yet another programming language, you can complete this task with the bash commands you probably know already.
With bash you can
increment a sequence number n: $((++n))
read all lines from a file foo into a variable l: while read -r l;do ...;done <foo, where the option -r serves to treat backslashes as just characters.
print formatted output to a line: printf "plain text %i %s\n" number string
Now suppose you want to enclose your sequence number in parentheses, and format them to 8 digits with leading zeroes, then you combine all this to get:
n=0;while read -r l;do printf "(%08i) %s\n" $((++n)) "$l";done <foo >numberedfoo
Note that you do not need to initialize the variable n to use it as a sequence number further on. But if you experiment with this command a few times without reinitializing n, your lines will be numbered from where your previous try stopped incrementing.
Finally, if you don't like the C-like formatting syntax of printf, just use plain echo, and leave the formatting to bash variable expansion. Here is how to format a number like in the command above (do type a space before the -, and a ; before the echo) :
nformat="0000000$n"; echo "(${nformat: -8}) ...";

Fastest way to extract pattern

What is the fastest way to extract a substring of interest from input such as the following?
MsgTrace(65/26)noop:user=xxx=INBOX:cmd=534
ImapFetchComplete(56/39)user=xxxxxxxxxx
Desired output (i.e., the :-terminated string following the string MsgTrace(65/26) in this example):
noop
I tried the following, but without success:
egrep -i "[a-zA-Z]+\(.*\)[a-z]+:"
grep by default returns the entire line when a match is found on a given input line.
While option -o restricts the output to only that part of the line that the regex matched, that is still not enough in this case, because you want a substring of that match.
However, since you're on Linux, you can use GNU grep's -P option (for support of PCREs, Perl-compatible regular expression), which allows extracting a submatch by way of features such as \K (drop everything matched so far) and (?=...) (a look-ahead assertion that does not contribute to the match):
$ grep -Po "[a-zA-Z]\(.*\)\K[a-z]+(?=:)" <<'EOF'
MsgTrace(65/26)noop:user=xxx=INBOX:cmd=534
ImapFetchComplete(56/39)user=xxxxxxxxxx
EOF
noop # output
Optional background information:
Ed Morton points out (in a since-deleted comment) that GNU grep's man page still calls the -P option "highly experimental" that may "warn of unimplemented features", but the option has been around for years, and in practice I have yet to see a warning or a performance problem - YMMV.
In the case at hand, the above command even outperforms sed and awk solutions - see NeronLeVelu's helpful performance comparison.
The interesting article Ed points to discusses a potential performance problem that can surface with regex engines such as used by grep -P (via the PCRE library), Perl itself, and many other widely used (and mature) regex engines, such as in Python, Ruby, and PHP:
In short: the recursive backtracking algorithm employed by these engines can result in severe performance degradation with "pathological" regexes that string together long sequences of subexpressions with variable-length quantifiers, such as (a longer version of) a?a?a?a?aaaa to match aaaa.
The article argues that backtracking is only truly required when a regex contains backreferences, and that a different, much faster algorithm should be employed in their absence.
You could try this:
$ sed -n 's/[[:alpha:]]*([^)]*)\([[:lower:]]*\):.*/\1/p' file
noop
It's portable to all POSIX seds and doesn't employ PCREs, just BREs, so the regexp matching part at least should be fast.
Little quick and dirty test on a 2469120 lines text of such a sample entry give grep -PO as winner
time sed -n -e 's/^MsgTrace[^)]\{4,\})//;t M' -e 'b' -e ':M' -e 's/:.*//p' YourFile >/dev/null
real 0m7.61s
user 0m7:10s
sys 0m0.13s
time awk -F ':' '/^MsgTrace/{ sub( /.*)/, "", $1); print $1}' YourFile >/dev/null
real 0m17.43s
user 0m16.19s
sys 0m0.17s
time grep -Po "[a-zA-Z]\(.*\)\K[a-z]+(?=:)" YourFile >/dev/null
real 0m6.72s
user 0m6.23s
sys 0m0.11s
time sed -n 's/[[:alpha:]]*([^)]*)\([[:lower:]]*\):.*/\1/p' YourFile >/dev/null
real 0m17.43s
user 0m16.29s
sys 0m0.12s
time grep -Po '(?<=MsgTrace\(65/26\)).*?(?=:)' YourFile >/dev/null
real 0m16.38s
user 0m15.22s
sys 0m0.15s
for #EdMorton question (i redo the same original sed to have compare value in same context of machine load). The exact string is lot faster, i imagine that sed try several combination before selecting which is the longest one for all criteria where a .*l give lot more possibility than pool is full
time sed -n -e 's/^MsgTrace([^)]\{3,\})//;T' -e 's/:.*//p' YourFile >/dev/null
real 0m7.28s
user 0m6.60s
sys 0m0.13s
time sed -n -e 's/^[[:alpha:]]*([^)]\{3,\})//;T' -e 's/:.*//p' YourFile >/dev/null
real 0m10.44s
user 0m9.67s
sys 0m0.14s
time sed -n -e 's/^[[:alpha:]]*([^)]*)//;T' -e 's/:.*//p' YourFile >/dev/null
real 0m10.54s
user 0m9.75s
sys 0m0.11s

How to input a command's result as a string argument in sed

i want to execute a command as follows on my bash terminal:
sed -i '6i `sed '1!d' input.in`' out
with which i can insert at line 6 of file out (with replacing -i option) the result of the sed '%1!d' input.in command. I haven't found anything useful, and have tried both `com`, $(com) and com | sed -i '6i ' out, where com stands for sed '%1!d' input.in. I don't have any problem changing the syntax of the whole command but i want it to be written in one line on terminal use sed.
Thanks for listening,
awaiting your answer.
For EdMorton:
Example Input:
input.in:
into a lake.
out:
Mary was runing around a pond and fell
into a lake.
Mary fell into a what?
Desired Output:
Mary was runing around a pond and fell
into a lake.
Mary fell into a what?
into a lake.
Try using r on standard input instead of i.
sed '%1!d' input.in |
sed -i '6r /dev/stdin' out
If your platform doesn't support /dev/stdin or /dev/fd/0, see if your sed supports - to mean standard input ... or, in the worst case, resort to a temporary file.
As commenters have already pointed out, %1!d does not appear to be a valid command in most sed dialects, but that is basically unimportant here. (If you mean to print just the first line, maybe you mean sed '1!d', although sed 'p;q' does that more efficiently.)
sed is for simple substitutions on individual lines, that is all. For anything else you should be using awk.
Given this modified input file
$ cat input.in
a Windows folder C:\Windows\Temp
Here is what the sed solution you posted in your comments does:
$ sed '1!d' input.in > temp.of.in && sed "6i `cat temp.of.in`" out
Mary was runing around a pond and fell
into a lake.
Mary fell into a what?
a Windows folder C:WindowsTemp
and here is what an awk solution does more efficiently and accurately and without a temp file:
$ awk 'NR==1{x=$0;nextfile} FNR==6{print x} 1' input.in out
Mary was runing around a pond and fell
into a lake.
Mary fell into a what?
a Windows folder C:\Windows\Temp
Notice the awk solution preserved the path-separator backslashes while the sed one stripped them. Also note that you should really add && rm temp.of.in to the end of your sed command line to clean up the temp file and you should be using $(..) to execute your command, not obsolete backticks.
The awk solution uses GNU awk for ;nextfile, with other awks you'd replace that with }NR==FNR{next or similar but since you are using GNU sed I assume you have GNU awk too.
Note that if you DID have a burning desire to use sed and accept it won't exactly reproduce the input, there are simpler, more efficient ways to do what your current script does, e.g.:
sed "6i $(head -1 input.in)" out
or even your original idea, just rewritten to remove the obsolete backticks and negative logic of 1!d:
sed "6i $(sed -n '1p' input.in)" out
But seriously - just use awk. For anything other than simple substitutions on individual lines it's much more robust, efficient, clear, portable, extensible, etc. etc. than sed.
EDIT To address the questions in your comments:
Can you explain the arguments on awk.
There are no arguments, just a script that says: If this is the first line read from the first file save it in variable x then move on to the next file. If this is line 6 of the 2nd file print the contents of variable x. For every line of the 2nd file, print it (the 1 is idiomatic but a bit tricky at first glance - it's a true condition so it invokes the default action of printing the current input, equivalent to just writing {print}.
how can i replace the out file with the output (without using '>') as the option -i does on sed and avoid printing it to stdout? Just like GNU sed has -i, GNU awk has -i inplace. Be careful though because, just like with sed, it applies to every input file so if you don't print the contents of the first file then when the script is done the first file will be empty. There's various was to deal with that, including simply printing the lines from file 1 or turning inplace editing on/off in BEGINFILE/ENDFILE blocks, see https://www.gnu.org/software/gawk/manual/gawk.html#Extension-Sample-Inplace, but IMHO awk 'script' file1 file2 > temp && mv temp file2 is the simplest and clearest as well as being portable to all awks/seds/whatever.
Also if there is a multiline solution like "take lines 1 to 4" from "input.in" and drop them on line 6 of "out"? No problem:
.
awk '
NR==FNR { if (NR<=4) x=x $0 ORS; else nextfile }
FNR==6 { printf "%s", x }
{ print }
' input.in out
I changed the 1 from the previous script to { print } for clarity.

sed explanation so I can recreate a bit of code?

Can someone please explain the following sed command?
title=$(wget -q -O - https://twitter.com/intent/user?user_id=$ID | sed -n 's/^.*<title>\(.*\) on Twitter<.title>.*$/\1/p')
printf "%s\n" "$title"
I tried (and failed terribly) to recreate it because I thought I understood what was going on in the code. So I wrote (well, more modded) it to be the following:
data-user-id=$(wget -q -O - https://twitter.com/$Username | sed -n 's/^.*"data-user-id">\([^<]*\)<.*$/\1/p')
printf "%s\n" "$data-user-id"
Obviously it errored because the syntax is wrong or something. But I'm trying to understand what is going on so I can make my own variant of it.
P.S. I can't just use the API for this due to how everything needs to be configured.
Give a try to this:
wget -q -O - https://twitter.com/"${Username}" | sed -n '/data-screen-name=.'"${Username}"'".*data-user-id=/I {s/^.*data-screen-name=.'"${Username}"'".*data-user-id="\([0-9]*\)".*$/\1/Ip;q}'
128700677
data-user-id is present in several lines, so it is needed to select a line where data-screen-name=Username
sed is using regular expression, there are 2 good tutorials to start with:
Regular Expressions
Sed - An Introduction and Tutorial by Bruce Barnett
A different sed script with a different output:
Username="StackOverflow"
wget -q -O - https://twitter.com/"${Username}" | sed -n '/data-screen-name=.'"${Username}"'".*data-user-id=/I {p;q}'
data-screen-name="StackOverflow" data-name="Stack Overflow" data-user-id="128700677"
-n instructs sed to not print anything, except when p command is used.
. means any char.
* applies to the previous char in the regex and it means zero or any number of this char.
.* means zero or any number of any char.
/data-screen-name=.'"${Username}"'".*data-user-id=/ select lines which contains data-screen-name= and any one char (.) and StackOverflow and " char and zero or any number of any char (.*) and data-user-id=.
/I means ignore case.
{p;q} are commands executed when above regex is true.
p prints the current line.
q exits the sed script.
The first sed script at the top contains an additional s/regex/replacement/ to clean up the line.
The additional elements used:
^ means the start of the line.
\( ... \) are used to define a group.
"\([0-9]*\)" is a group made of only digits, surrended with 2 " which are not part of the group. It is the first group found in the regex, so it can be referenced in the replacement part with \1.
Assuming the title of the page is "foo on Twitter", it extracts "foo" from it.
But use XMLStarlet instead, since it allows you to specify XPath to extract the data instead of having to poke around with regular expressions.

Convert string to hexadecimal on command line

I'm trying to convert "Hello" to 48 65 6c 6c 6f in hexadecimal as efficiently as possible using the command line.
I've tried looking at printf and google, but I can't get anywhere.
Any help greatly appreciated.
Many thanks in advance,
echo -n "Hello" | od -A n -t x1
Explanation:
The echo program will provide the string to the next command.
The -n flag tells echo to not generate a new line at the end of the "Hello".
The od program is the "octal dump" program. (We will be providing a flag to tell it to dump it in hexadecimal instead of octal.)
The -A n flag is short for --address-radix=n, with n being short for "none". Without this part, the command would output an ugly numerical address prefix on the left side. This is useful for large dumps, but for a short string it is unnecessary.
The -t x1 flag is short for --format=x1, with the x being short for "hexadecimal" and the 1 meaning 1 byte.
If you want to do this and remove the spaces you need:
echo -n "Hello" | od -A n -t x1 | sed 's/ *//g'
The first two commands in the pipeline are well explained by #TMS in his answer, as edited by #James. The last command differs from #TMS comment in that it is both correct and has been tested. The explanation is:
sed is a stream editor.
s is the substitute command.
/ opens a regular expression - any character may be used. / is
conventional, but inconvenient for processing, say, XML or path names.
/ or the alternate character you chose, closes the regular expression and
opens the substitution string.
In / */ the * matches any sequence of the previous character (in this
case, a space).
/ or the alternate character you chose, closes the substitution string.
In this case, the substitution string // is empty, i.e. the match is
deleted.
g is the option to do this substitution globally on each line instead
of just once for each line.
The quotes keep the command parser from getting confused - the whole
sequence is passed to sed as the first option, namely, a sed script.
#TMS brain child (sed 's/^ *//') only strips spaces from the beginning of each line (^ matches the beginning of the line - 'pattern space' in sed-speak).
If you additionally want to remove newlines, the easiest way is to append
| tr -d '\n'
to the command pipes. It functions as follows:
| feeds the previously processed stream to this command's standard input.
tr is the translate command.
-d specifies deleting the match characters.
Quotes list your match characters - in this case just newline (\n).
Translate only matches single characters, not sequences.
sed is uniquely retarded when dealing with newlines. This is because sed is one of the oldest unix commands - it was created before people really knew what they were doing. Pervasive legacy software keeps it from being fixed. I know this because I was born before unix was born.
The historical origin of the problem was the idea that a newline was a line separator, not part of the line. It was therefore stripped by line processing utilities and reinserted by output utilities. The trouble is, this makes assumptions about the structure of user data and imposes unnatural restrictions in many settings. sed's inability to easily remove newlines is one of the most common examples of that malformed ideology causing grief.
It is possible to remove newlines with sed - it is just that all solutions I know about make sed process the whole file at once, which chokes for very large files, defeating the purpose of a stream editor. Any solution that retains line processing, if it is possible, would be an unreadable rat's nest of multiple pipes.
If you insist on using sed try:
sed -z 's/\n//g'
-z tells sed to use nulls as line separators.
Internally, a string in C is terminated with a null. The -z option is also a result of legacy, provided as a convenience for C programmers who might like to use a temporary file filled with C-strings and uncluttered by newlines. They can then easily read and process one string at a time. Again, the early assumptions about use cases impose artificial restrictions on user data.
If you omit the g option, this command removes only the first newline. With the -z option sed interprets the entire file as one line (unless there are stray nulls embedded in the file), terminated by a null and so this also chokes on large files.
You might think
sed 's/^/\x00/' | sed -z 's/\n//' | sed 's/\x00//'
might work. The first command puts a null at the front of each line on a line by line basis, resulting in \n\x00 ending every line. The second command removes one newline from each line, now delimited by nulls - there will be only one newline by virtue of the first command. All that is left are the spurious nulls. So far so good. The broken idea here is that the pipe will feed the last command on a line by line basis, since that is how the stream was built. Actually, the last command, as written, will only remove one null since now the entire file has no newlines and is therefore one line.
Simple pipe implementation uses an intermediate temporary file and all input is processed and fed to the file. The next command may be running in another thread, concurrently reading that file, but it just sees the stream as a whole (albeit incomplete) and has no awareness of the chunk boundaries feeding the file. Even if the pipe is a memory buffer, the next command sees the stream as a whole. The defect is inextricably baked into sed.
To make this approach work, you need a g option on the last command, so again, it chokes on large files.
The bottom line is this: don't use sed to process newlines.
echo hello | hexdump -v -e '/1 "%02X "'
Playing around with this further,
A working solution is to remove the "*", it is unnecessary for both the original requirement to simply remove spaces as well if substituting an actual character is desired, as follows
echo -n "Hello" | od -A n -t x1 | sed 's/ /%/g'
%48%65%6c%6c%6f
So, I consider this as an improvement answering the original Q since the statement now does exactly what is required, not just apparently.
Combining the answers from TMS and i-always-rtfm-and-stfw, the following works under Windows using gnu-utils versions of the programs 'od', 'sed', and 'tr':
echo "Hello"| tr -d '\42' | tr -d '\n' | tr -d '\r' | od -v -A n -tx1 | sed "s/ //g"
or in a CMD file as:
#echo "%1"| tr -d '\42' | tr -d '\n' | tr -d '\r' | od -v -A n -tx1 | sed "s/ //g"
A limitation on my solution is it will remove all double quotes (").
"tr -d '\42'" removes quote marks that the Windows 'echo' will include.
"tr -d '\r'" removes the carriage return, which Windows includes as well as '\n'.
The pipe (|) character must follow immediately after the string or the Windows echo will add that space after the string.
There is no '-n' switch to the Windows echo command.

Resources