sed takes all the strings passed as arguments to -e options and the contents of files passed to -f options and concatenates them together with newlines in between to make up the sed script to interpret.
perl (without -f) and GNU awk do the same.
For instance doing:
sed -eA -f file1 -e 'C
D' -f file2 -e 'F;G'
Where file1 contains B and file2 contains E is the same as running:
sed -e 'A
B
C
D
E
F;G'
If not passed any -e or -f argument, the first non-option argument is taken as the literal sed code to interpret, so it's also the same as:
sed 'A
B
C
D
E
F;G'
In the rudimentary sed language (from the 70s; itself inspired from ed from the 60s), commands can be separated with newlines (or with separate -es which is equivalent to adding newlines) and some of them can be separated with ;.
There are some exceptions for those commands that take arguments and some others for historical but not good reason.
You can't separate a :, or b or t command from the next with ; because in the original sed implementation and as still allowed by POSIX and still the case with many sed implementations:
b foo;bar
branches to the foo;bar label. That's not b foo followed by b ar like it is in GNU sed.
Same goes for r or w (or the w flag of the s command) with which even in GNU sed r foo;bar is to read from the foo;bar file.
The closing } can also not be followed by anything in the original implementation for no good reason.
Those are good reasons why you may want to use -e (and for some reason you want your command to be on a single line; in the csh shell from the late '70s, it was a real pain entering a multi-line command).
But in your:
sed -e 's/-/_/g' -e ':a; N; $!ba; s/\n//g' input.txt
You're using -e in places where it's not needed and not when they are needed (in non-GNU sed implementations).
Besides the fact that you're running those commands in the wrong order, syntactically, that code is neither standard nor portable. You should not have other commands on the same line after the : and b commands.
It also ends up loading the whole input in the pattern space, the size of which is limited in most sed implementations (and where it's not, it could end up using up all the system's memory for no good reason).
sed '
:a
$ ! {
N
b a
}
y/-/_/
s/\n//g' input.txt
Or if you need it on one line:
sed -e :a -e '$!{N;ba' -e '}' -e 'y/-/_/; s/\n//g' input
Would be syntactically and functionally correct with standard sed but load the whole input in memory.
But here you only need:
<input tr - _ | paste -sd '\0' -
Where tr transliterates -s to _s and paste joins the lines together each not using up more than a small buffer of memory.
Beware though that except with the busybox implementation of paste, that produces a non-empty output (one empty line) for an empty input.
If you're ready to swap those '70s tools with one from the (late) '80s (though it has kept evolving since), you could do:
perl -pe 'tr/-/_/; chomp unless eof' < input
Where we transliterate -s to _s (for sed devotees, y is provided as a synonym for tr) and chomps (removes the line delimiter from) all lines except the last.
perl -p is perl's sed mode, but contrary to sed, perl keeps the input line delimiter in the pattern space ($_ in perl), which allows us to remove it conditionally.
:a; N; $!ba; s/\n//g'is to define a label calleda; N; $!ba.sed -z 's/-/_/g; s/\n//g'should do what you want at least with GNU sed.awk -v ORS= '{gsub(/-/,"_"); print} END{if (NR) print RS}' input.txtwould do what you want in any awk without relying on an arcane pile of single-character instructions and without reading all of the input into memory and so would work for arbitrarily large input.