Why use 0d_ in DiffOrig in Vim? - vim

In the online manual of Vim, :help DiffOrig
will show the recommended command sequence to get changes of current editing file.
Here it is:
command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis
\ | wincmd p | diffthis
I wonder what the effect of 0d_ is. I tried 0d_ in normal mode, it works like dd, but I can't understand why it used here.

Let's explain it a bit: Suppose you have original foo.txt containing (with line numbers):
1 a
2 c
3 d
~
You have added a line containing “b” between lines 1 and 2:
:vert new creates a new, empty, buffer in a vertical split (:help :new)
:set bt=nofile makes it a scratch buffer (:help 'bt'). Note:
1 a | 1 ·<cursor here
2 b | ~
3 c | ~
4 d | ~
~ | ~
:r # inserts after current line, contents of alternate file (#), as stored on the file system. You haven't saved the other buffer, so you get original content. (:help alternate-file).
:help :r tells you that it always inserts after. Therefore:
1 a | 1
2 b | 2 a
3 c | 3 c
4 d | 4 d
~ | ~
:0d_ removes the first line. Why 0, I don't know it really, I would rather
write it :1d_. :help range tells:
When using a 0 (zero) this is interpreted as a 1 by most commands
The _ specifies that it goes to the black-hole register. See
:help :d about the :d ex command, it works linewise.
The rest is obvious.

Related

Calculating a sum of numbers in C shell

I'm trying to calculate a sum numbers positioned on separate lines using C shell.
I must do it with specific commands using pipes.
There is a number of commands: comand.. | comand.. | (comands...)
printing lines in the following form:
1
2
8
4
7
The result should be 22, since 1 + 2 + 8 + 4 + 7 = 22.
I tried ... | bc | tr "\n" "+" | bc, but it didn't work.
I can't use AWK, or variables. That is why I am asking for help.
You actually can use the C shell variables, as they are part of the syntax. Without using variables, you need to pipe, and pipe again:
your-command | sed '2~1 s/^/+/' | xargs | bc
The sed command prepends plus character to all lines starting from the second; xargs joins the lines as a sequence of arguments.
The SED expression can be improved to filter out non-numeric lines:
'/^[^0-9]\+$/ d; 2~1 s/\([0-9]\+\)/+\1/'

vimdiff: force line-by-line comparison (ignore supposedly missing/additional lines)

How do I force vimdiff to always compare two files line-by-line without identifying added or deleted lines?
The problem is that if the diff between two files is large, but by chance two lines in the file match up, vimdiff thinks these lines are the same and just treats the rest as added or deleted lines, and the resulting diff is totally unusable. In my case, line i in file1 always corresponds to line i in file2, so vimdiff has no business finding added or deleted lines.
Following is a small example with two files containing the values of two variables three times each. Vimdiff erroneously matches up file1/line1 with file2/line3 and thinks some lines around it have been added or deleted. The diff (minus colors) then looks like this:
| 1 foo 8.1047 < del/new
| 2 bar 6.2343 < del/new
1 foo 0.0000 | 3 foo 0.0000 < match
2 bar 5.3124 | 4 bar 1.4452 < wrong
3 foo 4.5621 | < new/del
4 bar 6.3914 | < new/del
5 foo 1.0000 | 5 foo 1.0000 < match
6 bar 6.3212 | 6 bar 7.2321 < wrong
What I want, however, is the following, with all lines marked as wrong except for the matching lines 5:
1 foo 0.0000 | 1 foo 8.1047 < wrong
2 bar 5.3124 | 2 bar 6.2343 < wrong
3 foo 4.5621 | 3 foo 0.0000 < wrong
4 bar 6.3914 | 4 bar 1.4452 < wrong
5 foo 1.0000 | 5 foo 1.0000 < match
6 bar 6.3212 | 6 bar 7.2321 < wrong
As I was copying this example to try it, I noticed that vimdiff will do what you want if you have the line number associated with each line.
Therefore, you can use cat to add the line number and then diff:
cat -n file1 > file1_with_line_no
cat -n file2 > file2_with_line_no
vimdiff file1_with_line_no file2_with_line_no
The output is then as you want (shown with diff for easy copying to here):
diff file1_with_line_no file2_with_line_no --side-by-side
1 foo 0.0000 | 1 foo 8.1047
2 bar 5.3124 | 2 bar 6.2343
3 foo 4.5621 | 3 foo 0.0000
4 bar 6.3914 | 4 bar 1.4452
5 foo 1.0000 5 foo 1.0000
6 bar 6.3212 | 6 bar 7.2321
In bash you can add this to your .bashrc so you can use linediff from the command line to just normally call a diff between two files with the above:
linediff() {
if [ -z "$1" ] || [ -z "$2" ]; then return; fi
f1=$(basename "$1")
f2=$(basename "$2")
cat -n "$1" > "/tmp/$f1"
cat -n "$2" > "/tmp/$f2"
vimdiff "/tmp/$f1" "/tmp/$f2"
rm "/tmp/$f1" "/tmp/$f2"
}
and now linediff file1 file2 will do the above and clean up after.
How about using diffchar.vim plugin? It compares line-by-line in non-diff mode. Please open 2 files on 2 windows and then just press F7. By default, it tries to find the differences by characters in a line, but you can change the difference units, words or something.
Vim relies on the external diff command to analyze the two files, so you can influence the result via a different tool that uses a different algorithm. You can configure that via the 'diffexpr' option; the tool's output has to be in "ed" style. Cp. :help diff-diffexpr.
Note that this only affects the lines added / changed / deleted; for displaying the character differences in a changed line itself, Vim does that on its own.
Unfortunately, I don't know any alternative diff tool that could provide such output, but maybe others can fill in that.

Relative number in vim

I noticed a strange behavior in vim regarding the relative numbers:
:set nu
1
2
3 _
4
5
6
~
:set relativenumber
2
1
3 _
1
2
3
~
:set nonu
2
1
0 _
1
2
3
~
I was asking myself why sometime I see the absolute current line number and sometime I see 0 which is pretty useless.
Is this behavior normal and how to correctly number and relativenumber?
It's the expected behavior. From :help number_relativenumber
The 'relativenumber' option changes the displayed number to be
relative to the cursor. Together with 'number' there are these
four combinations (cursor in line 3):
'nonu' 'nu' 'nonu' 'nu'
'nornu' 'nornu' 'rnu' 'rnu'
|apple | 1 apple | 2 apple | 2 apple
|pear | 2 pear | 1 pear | 1 pear
|nobody | 3 nobody | 0 nobody |3 nobody
|there | 4 there | 1 there | 1 there
That behavior depends on how number and relativenumber are combined.
Everything is explained in :help number_relativenumber, a subsection of :help 'number' that you would have found if you had tried a little harder.

Joining a pair of lines with specific starting points

I know that with sed I can print
cat current.txt | sed 'N;s/\n/,/' > new.txt
A
B
C
D
E
F
to
A,B
C,D
E,F
What I would like to do is following:
A
B
C
D
E
F
to
A,D
B,E
C,F
I'd like to join 1 with 4, 2 with 5, 3 with 6 and so on.
Is this possible with sed? Any idea how it could be achieved?
Thank you.
Try printing in columns:
pr -s, -t -2 current.txt
This is longer than I was hoping, but:
$ lc=$(( $(wc -l current.txt | sed 's/ .*//') / 2 ))
$ paste <(head -"$lc" current.txt) <(tail -"$lc" current.txt) | column -t -o,
The variable lc stores the number of lines in current.txt divided by two. Then head and tail are used to print lc first and lc last lines, respectively (i.e. the first and second half of the file); then paste is used to put the two together and column changes tabs to commas.
An awk version
awk '{a[NR]=$0} NR>3 {print a[NR-3]","$0}' current.txt
A,D
B,E
C,F
This solution is easy to adjust if you like other interval.
Just change NR>3 and NR-3 to desired number.

"Minus" operation on two files using Linux commands

I have 4 files sorted alphabetically, A, B, C, and D.
These files contain a single string on each line.
Essentially, what needs to happen is that anything in B gets deleted from A.
The result of that will then be stripped of anything in C.
And similarly, the result of that will be stripped of D.
Is there a way to this using Linux commands?
comm is good for this, either:
cat B C D | sort | comm -2 -3 A -
or:
comm -2 -3 A B | comm -2 -3 - C | comm -2 -3 - D
depending on what's easier/clearer for your script.
grep -x -v -f B A | grep -x -v -f C | grep -x -v -f D
The -v switch is an inverse match (i.e. match all except). The -f switch takes a file with a list of patterns to match. The -x switch forces it to match whole lines (so that lines that are substrings of other lines don't cause the longer lines to be removed).
Look at the join command. Read its man page and you should find what you seek.
join A B | join - C | join - D

Resources