How do I prepend a zero to a digit with sed? - sed

I would like to add a zero to the middle of a line of formatted text using sed or awk.
Example Input File
line 1
line 2
line 3
Expected Output
line 01
line 02
line 03

There is some ambiguity with your question but how about using printf with awk to pad the second field with zeros:
$ awk 'NF==2 { printf "%s %02d\n", $1, $2}' file
line 01
line 02
line 03
line 10
line 100
$ awk 'NF==2 { printf "%s %04d\n", $1, $2}' file
line 0001
line 0002
line 0003
line 0010
line 0100
If you don't want blank lines stripped do awk 'NF==2 { printf "%s %04d\n", $1, $2} NF!=2' file.

Use a POSIX Character Class with Backreference
Given /tmp/foo containing:
line 1
line 2
line 3
you can call sed 's/[[:digit:]]/0&/' /tmp/foo in order to get the following output:
line 01
line 02
line 03

In awk this is quite easy:
awk '{print $1, 0$2}' input.txt > output.txt
In this case the input text you have shown is in the file input.txt and I saved it to a new file output.txt.
This is also an easy command to understand. The $1 variable is your "line" text and the $2 variable is your number. This command assumes there are no spaces in your "line" text. Are there?

The simplest answer to your question as posted is:
sed 's/ / 0/'
If that doesn't do it, post some more representative input and expected output as there are many things you might need to take into account.

This might work for you (GNU sed):
sed 's/\</0/2' file

awk 'NF==2{$2="0"$2}1' your_file

Related

Replace specific line numbers using sed

I use below to replace text on line numbers 29 to 32:
sed -i '29,32 s/^ *#//' file
How can I further add line numbers i.e. line 35 to 38, line 43 & line 45 in above command?
With GNU sed. m is here a label.
sed -i '29,32bm;35,38bm;43bm;45bm;b;:m;s/^ *#//' file
From man sed:
b label: Branch to label; if label is omitted, branch to end of script.
: label: Label for b and t commands.
This might work for you (GNU sed):
cat <<\! | sed 's:$:s/^ *#//:' | sed -f - -i file
29,32
35,38
43,45
!
Create a here-document with the line ranges you desire and pass these to a sed script that creates another sed script which is run against the desired file.
The here-document could be replaced by a file:
sed 's:$:s/^ *#//:' lineRangeFile | sed -f - -i file

How to force sed to print what it does with my file?

How to force sed to print what it does with my file?
My text01.txt file:
aaa
bbb
ccc
ddd
c
ee
My code:
sed -i 's/c/X/g' ./text01.txt
I want to get in terminal something like this:
sed: line 3 change ccc to XXX
sed: line 5 change c to X
sed -i"bak" 's/c/X/g' text01.txt && diff text01.txt text01.txtbak
will give you a diff summary. like:
3c3
< XXX
---
> ccc
5c5
< X
---
> c
You can read diff man page, to adjust the diff output, e.g. with -c/-u/-y... options as you like.
If you want to get exactly same format you described, you can do some work on diff output as well.
This comes pretty close to your requirement:
$ paste <(cat -n text01.txt) <(sed 's/c/X/g' ./text01.txt)
1 aaa aaa
2 bbb bbb
3 ccc XXX
4 ddd ddd
5 c X
6 ee ee
cat -n prepends line numbers, and the paste command with process substitution prints the file and the sed output next to each other.
Or, more elaborate, with awk:
awk '{ getline mod_line < ARGV[2]
if ($0 != mod_line) {
printf "sed line %d change %s to %s\n", NR, $0, mod_line }
}' text01.txt <(sed 's/c/X/g' text01.txt)
This reads, for each line of text01.txt, the corresponding line as modified by sed. If they are different, the line number and both lines get printed:
sed line 3 change ccc to XXX
sed line 5 change c to X
plus an awk warning because it tries to close an anonymous pipe – this can be suppressed by redirecting stderr, i.e., appending 2> /dev/null to the command.
The closest thing to sed "built-in" debugging is the l command, which prints the current content of the pattern space. If you'd like to go all in, there are proper debuggers, for example sedsed.
This might work for you (GNU sed):
sed -i -e 'h;/c/!b;s//X/g;H;x;s/\n/ to /;s/^/sed: changed /w/dev/stdout' -e 'x' file
This makes a copy of each line in the hold space (HS) and if the substitution pattern does not match, no further action takes place. Otherwise, the substitution is made on the line in the pattern space (PS) and this is appended to the HS. Focus is then changed to the HS and format of before and after effected. The formated line is then written out to the standard output i.e. the terminal and finally focus is reverted to the PS so that the substituted line is included in the original updated file.

Sed Process Substitution on Insert - Without Backslashes

I have function that prints a header that needs to be applied across several files, but if I utilize a sed process substitution the lines prior to the last have a backslash \ on them.
E.g.
function print_header() {
cat << EOF
-------------------------------------------------------------------
$(date '+%B %d, %Y # ~ %r') ID:$(echo $RANDOM)
EOF
}
If I then take a file such as test.txt:
line 1
line 2
line 3
line 4
line 5
sed "1 i $(print_header | sed 's/$/\\/g')" test.txt
I get:
-------------------------------------------------------------------\
November 24, 2015 # ~ 11:18:28 AM ID:13187
line 1
line 2
line 3
line 4
line 5
Notice the troublesome backslash at the end of the first line, I'd like to not have that backslash appear. Any ideas?
I would use cat for that:
cat <(print_header) file > file_with_header
This behavior depends on the sed dialect. Unfortunately, it's one of the things which depends on which version you have.
To simplify debugging, try specifying verbatim text. Here's one from a Debian system.
vnix$ sed '1i\
> foo\
> bar' <<':'
> hello
> goodbye
> :
foo
bar
hello
goodbye
Your diagnostics appear to indicate that your sed dialect does not in fact require the backslash after the first i.
Since you are generating the contents of the header programmatically anyway, my recommended solution would be to refactor the code so that you can avoid this conundrum. If you don't want cat <<EOF test.txt then maybe experiment with sed 1r/dev/stdin' <<EOF test.txt (I could not get 1r- to work, but /dev/stdin should be portable to any Linux.)
Here is my kludgy fix, if you can find something more elegant I'll gladly credit you:
sed "1 i $(print_header | sed 's/$/\\/g;$s/$/\x01/')" test.txt | tr -d '\001'
This puts an unprintable SOH (\x01) ascii Start Of Header character after the inserted text, that precludes the backslashes and then I run it over tr to delete the SOH chars.

Replace multiple lines with sed

I have in sample.txt the following content
abc
efg
hij
klm
nop
qrs
I have tried replacing abc with other text with
sed -i '/abc/c\This line is removed by the admin.' sample.txt
Output:
This line is removed by the admin.
efg
hij
klm
nop
qrs
It worked but for a single line.
But I am wondering how could I replace a given set of lines say 1 to 3 using sed?
If you know the line numbers, you prepend them to your pattern, like so:
sed -i '4 s/abc/c\This line is removed by the admin./' sample.txt
The above will change line 4. If you want to change ranges (say, lines 5-10), enter the start and end line numbers with a comma between:
sed -i '5,10 s/abc/c\This line is removed by the admin./' sample.txt
$ represents the last line in the file so if you wanted say, line 100 to the end:
sed -i '100,$ s/abc/c\This line is removed by the admin./' sample.txt
You may find this link helpful. Look for the section on Ranges by line number.
If your only criterion is the line numbers, then you can specify them like so:
sed -i '1,3 s/.*/This line is removed by the admin./' sample.txt
Here is an awk solution if you like to try:
awk 'NR>=1 && NR<=3 {$0="This line is removed by the admin."}1' file
This line is removed by the admin.
This line is removed by the admin.
This line is removed by the admin.
klm
nop
qrs
To write it back to the file
awk 'NR>=1 && NR<=3 {$0="This line is removed by the admin."}1' file > tmp && mv tmp file
This might work for you (GNU sed):
sed '1,3c\replace lines 1 to 3 with this single line' file
if you want to replace each line within the range use:
sed $'1,3{\\athis replaces the original line\nd}' file
or perhaps more easily:
sed '1,3s/.*/this replaces the original line/' file

Sed or awk: how to call line addresses from separate file?

I have 'file1' with (say) 100 lines. I want to use sed or awk to print lines 23, 71 and 84 (for example) to 'file2'. Those 3 line numbers are in a separate file, 'list', with each number on a separate line.
When I use either of these commands, only line 84 gets printed:
for i in $(cat list); do sed -n "${i}p" file1 > file2; done
for i in $(cat list); do awk 'NR==x {print}' x=$i file1 > file2; done
Can a for loop be used in this way to supply line addresses to sed or awk?
This might work for you (GNU sed):
sed 's/.*/&p/' list | sed -nf - file1 >file2
Use list to build a sed script.
You need to do > after the loop in order to capture everything. Since you are using it inside the loop, the file gets overwritten. Inside the loop you need to do >>.
Good practice is to or use > outside the loop so the file is not open for writing during every loop iteration.
However, you can do everything in awk without for loop.
awk 'NR==FNR{a[$1]++;next}FNR in a' list file1 > file2
You have to >>(append to the file) . But you are overwriting the file. That is why, You are always getting 84 line only in the file2.
Try use,
for i in $(cat list); do sed -n "${i}p" file1 >> file2; done
With sed:
sed -n $(sed -e 's/^/-e /' -e 's/$/p/' list) input
given the example input, the inner command create a string like this: `
-e 23p
-e 71p
-e 84p
so the outer sed then prints out given lines
You can avoid running sed/awk in a for/while loop altgether:
# store all lines numbers in a variable using pipe
lines=$(echo $(<list) | sed 's/ /|/g')
# print lines of specified line numbers and store output
awk -v lineS="^($lines)$" 'NR ~ lineS' file1 > out