如何用Vim编辑二进制文件?


77

有没有办法以某种十六进制模式编辑二进制文件?

例如,如果我具有由示出的一些二进制数据xxdhexdump -C像这样:

$ hexdump -C a.bin | head -n 5
00000000  cf fa ed fe 07 00 00 01  03 00 00 80 02 00 00 00  |................|
00000010  12 00 00 00 40 05 00 00  85 00 20 00 00 00 00 00  |....@..... .....|
00000020  19 00 00 00 48 00 00 00  5f 5f 50 41 47 45 5a 45  |....H...__PAGEZE|
00000030  52 4f 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |RO..............|
00000040  00 00 00 00 01 00 00 00  00 00 00 00 00 00 00 00  |................|

$ xxd a.bin | head -n 5
0000000: cffa edfe 0700 0001 0300 0080 0200 0000  ................
0000010: 1200 0000 4005 0000 8500 2000 0000 0000  ....@..... .....
0000020: 1900 0000 4800 0000 5f5f 5041 4745 5a45  ....H...__PAGEZE
0000030: 524f 0000 0000 0000 0000 0000 0000 0000  RO..............
0000040: 0000 0000 0100 0000 0000 0000 0000 0000  ................

如果我想在特定位置更改值,这种视图将有助于找到正确的位置,例如,当更改位置在某个已知字符串附近时。

Answers:


89

最简单的方法是使用该binary选项。来自:help binary

This option should be set before editing a binary file.  You can also
use the -b Vim argument.  When this option is switched on a few
options will be changed (also when it already was on):
        'textwidth'  will be set to 0
        'wrapmargin' will be set to 0
        'modeline'   will be off
        'expandtab'  will be off
Also, 'fileformat' and 'fileformats' options will not be used, the
file is read and written like 'fileformat' was "unix" (a single <NL>
separates lines).
The 'fileencoding' and 'fileencodings' options will not be used, the
file is read without conversion.

[..]

When writing a file the <EOL> for the last line is only written if
there was one in the original file (normally Vim appends an <EOL> to
the last line if there is none; this would make the file longer).  See
the 'endofline' option.

如果您不这样做,而您的环境正在使用多字节编码(例如,大多数人使用的是UTF-8),则Vim会尝试对文本进行编码,这通常会导致文件损坏。

您可以通过打开文件并仅使用进行验证:w。现在已更改。
如果将LANG和设置LC_ALLC(ASCII),由于Vim不需要进行任何多字节编码,因此Vim不会进行任何转换并且文件保持不变(尽管仍然会添加换行符)。

我个人也喜欢禁用 set wrap二进制文件,尽管其他人可能喜欢启用它。YMMV。另一个有用的事情是:set display=uhex。来自:help 'display'

uhex            Show unprintable characters hexadecimal as <xx>
                instead of using ^C and ~C.

最后,您可以使用%B:set rulerformat=0x%B)在标尺中的光标下方显示字符的十六进制值。

更先进: xxd

您可以使用该xxd(1)工具将文件转换为更具可读性的格式,然后(这是重要的一点),解析已编辑的“可读格式”,然后将其写回二进制数据。xxd是的一部分vim,因此如果您已vim安装也应该拥有xxd

要使用它:

$ xxd /bin/ls | vi -

或者,如果您已经打开文件,则可以使用:

:%!xxd

现在进行更改,您需要在显示屏的左侧(十六进制数字)进行操作,在写入时将忽略右侧的更改(可打印的表示形式)。

要保存它,请使用xxd -r

:%!xxd -r > new-ls

这会将文件保存到new-ls

或将二进制文件加载到当前缓冲区中:

:%!xxd -r

来自xxd(1)

   -r | -revert
          reverse operation: convert (or patch) hexdump into  binary.   If
          not  writing  to stdout, xxd writes into its output file without
          truncating it. Use the combination -r -p to read plain hexadeci‐
          mal dumps without line number information and without a particu‐
          lar column layout. Additional  Whitespace  and  line-breaks  are
          allowed anywhere.

然后只需使用:w它来编写。(请注意binary 出于与上述相同的原因,您想在写入文件之前设置该选项)。

互补的按键绑定使此操作更简单:

" Hex read
nmap <Leader>hr :%!xxd<CR> :set filetype=xxd<CR>

" Hex write
nmap <Leader>hw :%!xxd -r<CR> :set binary<CR> :set filetype=<CR>

如果您使用的是gVim,也可以从菜单中使用,在“工具➙转换为十六进制”和“工具➙转换回”下。

VIM技巧的wiki有更多的信息和一些辅助脚本的页面。就个人而言,我认为如果经常编辑二进制文件,最好使用真正的十六进制编辑器。Vim 可以 胜任这项工作,但是显然不是为此而设计的,如果您在没有:set binaryVim的情况下编写,可能会破坏您的二进制文件!


4
答案很不错,但应该以“孩子们不要在家尝试这个”开头。
msw 16年

如果我需要删除一些字节怎么办?例如在二进制文件中间。
安东·K

我不知道Vim在做什么,但是尽管我没有做任何改动,但它仍将95KB的文本添加到200KB的二进制文件中。即使:set binary noeol fenc=utf-8。实际上,它是在打开文件之前立即说的[noeol] [converted]。为什么vim需要将缓冲区增大150%?我如何防止它破坏这样的文件?
Braden Best

唯一有效的方法是:r !xxd <file>(或$ xxd <file> | vim -)读取和:w !xxd -r > <file>写入,但这并不理想。
Braden Best

极好的答案。请注意,bless的网址无效。我在github.com/bwrsandman/Bless的 github上找到了它。
sonofagun

19

要以十六进制视图查看二进制文件的内容,请打开文件,打开二进制模式,然后通过以下xxd命令过滤缓冲区:

:set binary
:%!xxd

您可以在左侧区域进行更改(编辑十六进制数字),并准备好进行过滤xxd -r,最后保存文件:

:%!xxd -r
:w

如果打开后和关闭前的过滤步骤听起来很繁琐,并且您通常使用.bin扩展名的文件来执行此操作,则可以将其添加到vimrc中以使过程自动进行:

" for hex editing
augroup Binary
  au!
  au BufReadPre  *.bin let &bin=1
  au BufReadPost *.bin if &bin | %!xxd
  au BufReadPost *.bin set ft=xxd | endif
  au BufWritePre *.bin if &bin | %!xxd -r
  au BufWritePre *.bin endif
  au BufWritePost *.bin if &bin | %!xxd
  au BufWritePost *.bin set nomod | endif
augroup END

如果我按照这些指示(打开二进制文件,:%!xxd:%!xxd -r:w,didnt做任何改变!)然后写成二进制文件是一样的原...这是你的情况下(我测试/bin/ls)。我需要:set binary在保存之前使用(另请参阅我的答案,其中解释了原因)...也许这是我的vimrc中的内容?但是set binary
不管怎么说

1
如果您不希望使您的augroup~/.vim/plugin/binary.vim.vimrc
文件

如果您使用的是国外安装,则该augroup Binary列表位于5.5以后的Vim :help hex-editing:help using-xxd任何Vim中(1999年9月)。
bb010g

6

使用“ bvi”编辑器。 http://bvi.sourceforge.net/ (在每个Linux存储库中)。

$ apt-cache show bvi
[snip]
Description-en: binary file editor
 The bvi is a display-oriented editor for binary files, based on the vi
 text editor. If you are familiar with vi, just start the editor and begin to
 edit! If you never heard about vi, maybe bvi is not the best choice for you.

1
更高级的替代方法是bviplus,它具有vim控件。
安东·K


3

TL; DR回答

用Vim以二进制模式打开文件:

vim -b <file_to_edit>

在Vim中,进入十六进制编辑模式,如下所示:

:%!xxd -p

保存:

:%!xxd -p -r
:w

这会将缓冲区从十六进制模式转换回去,然后像平常一样保存文件。

注意-p选项。这避免了所有多余的可打印和地址模糊,而只是向您显示了十六进制。如果需要额外的上下文,只需省略-p即可。

小心不要以Vim二进制模式打开文件,因为在保存文件时,它将在文件末尾附加一个(通常是意外的)LF字符。


这实际上并没有添加其他答案中没有的任何内容。
赫尔·沃尔夫

5
真正的TL; DR已经存在:h using-xxd并且已经存在了v7.0001很长时间。如果人们搜索文档,则该站点的活跃性将降低。
汤米”,

1

看起来像一个方便的小vim插件,它使用一个临时文件来完成工作,该文件会自动为您来回写。

几年前,我发现了一个类似的插件,我对其进行了改进和改进以供自己使用。如果有人需要的话,我在这里包括了相关的代码。它也基于xxd工具。我敢肯定我上面链接的GitHub版本能更好地工作,但是我自己还没有真正使用过它,所以我想我也应该发布这个我肯定可以使用的版本。

此版本的源是vim wikia,尤其是此页面

这是代码:

"-------------------------------------------------------------------------------  
" Hexmode  
"-------------------------------------------------------------------------------  
" Creates an automatic hex viewing mode for vim by converting between hex dump  
" and binary formats. Makes editing binary files a breeze.  
"-------------------------------------------------------------------------------  
" Source: vim.wikia.com/wiki/Improved_Hex_editing  
" Author: Fritzophrenic, Tim Baker  
" Version: 7.1  
"-------------------------------------------------------------------------------  
" Configurable Options {{{1  
"-------------------------------------------------------------------------------  

" Automatically recognized extensions  
let s:hexmode_extensions = "*.bin,*.exe,*.hex"  

"-------------------------------------------------------------------------------
" Commands and Mappings {{{1
"-------------------------------------------------------------------------------

" ex command for toggling hex mode - define mapping if desired
command! -bar Hexmode call ToggleHex()
command! -nargs=0 Hexconfig edit $VIM\vimfiles\plugin\hexmode.vim | exe "normal 11G" | exe "normal zo"

nnoremap <C-H> :Hexmode<CR>
inoremap <C-H> <Esc>:Hexmode<CR>
vnoremap <C-H> :<C-U>Hexmode<CR>

"-------------------------------------------------------------------------------    
" Autocommands {{{1  
"-------------------------------------------------------------------------------  

if exists("loaded_hexmode")  
    finish  
endif  
let loaded_hexmode = 1  

" Automatically enter hex mode and handle file writes properly  
if has("autocmd")  
  " vim -b : edit binary using xxd-format  
  augroup Binary  
    au!  

    " set binary option for all binary files before reading them  
    exe "au! BufReadPre " . s:hexmode_extensions . " setlocal binary"

    " if on a fresh read the buffer variable is already set, it's wrong
    au BufReadPost *
          \ if exists('b:editHex') && b:editHex |
          \   let b:editHex = 0 |
          \ endif

    " convert to hex on startup for binary files automatically
    au BufReadPost *
          \ if &binary | Hexmode | endif

    " When the text is freed, the next time the buffer is made active it will
    " re-read the text and thus not match the correct mode, we will need to
    " convert it again if the buffer is again loaded.
    au BufUnload *
          \ if getbufvar(expand("<afile>"), 'editHex') == 1 |
          \   call setbufvar(expand("<afile>"), 'editHex', 0) |
          \ endif

    " before writing a file when editing in hex mode, convert back to non-hex
    au BufWritePre *
          \ if exists("b:editHex") && b:editHex && &binary |
          \  let oldro=&ro | let &ro=0 |
          \  let oldma=&ma | let &ma=1 |
          \  silent exe "%!xxd -r" |
          \  let &ma=oldma | let &ro=oldro |
          \  unlet oldma | unlet oldro |
          \ endif

    " after writing a binary file, if we're in hex mode, restore hex mode
    au BufWritePost *
          \ if exists("b:editHex") && b:editHex && &binary |
          \  let oldro=&ro | let &ro=0 |
          \  let oldma=&ma | let &ma=1 |
          \  silent exe "%!xxd" |
          \  exe "set nomod" |
          \  let &ma=oldma | let &ro=oldro |
          \  unlet oldma | unlet oldro |
          \ endif
  augroup END  
endif  

"-------------------------------------------------------------------------------
" Functions {{{1
"-------------------------------------------------------------------------------

" helper function to toggle hex mode
function! ToggleHex()
  " hex mode should be considered a read-only operation
  " save values for modified and read-only for restoration later,
  " and clear the read-only flag for now
  let l:modified=&mod
  let l:oldreadonly=&readonly
  let &readonly=0
  let l:oldmodifiable=&modifiable
  let &modifiable=1
  if !exists("b:editHex") || !b:editHex
    " save old options
    let b:oldft=&ft
    let b:oldbin=&bin
    " set new options
    setlocal binary " make sure it overrides any textwidth, etc.
    let &ft="xxd"
    " set status
    let b:editHex=1
    " switch to hex editor
    set sh=C:/cygwin/bin/bash
    %!xxd
  else
    " restore old options
    let &ft=b:oldft
    if !b:oldbin
      setlocal nobinary
    endif
    " set status
    let b:editHex=0
    " return to normal editing
    %!xxd -r
  endif
  " restore values for modified and read only state
  let &mod=l:modified
  let &readonly=l:oldreadonly
  let &modifiable=l:oldmodifiable
endfunction

" vim: ft=vim:fdc=2:fdm=marker
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.