Jak edytować pliki binarne za pomocą Vima?


77

Czy istnieje sposób edycji plików binarnych w jakimś trybie szesnastkowym?

Na przykład jeśli mam jakieś dane binarne pokazane przez xxdlub w hexdump -Cten sposób:

$ 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  ................

Jeśli chcę zmienić wartość w określonej pozycji, ten rodzaj widoku pomoże znaleźć właściwe miejsce, na przykład gdy pozycja do zmiany znajduje się w pobliżu jakiegoś znanego ciągu.

Odpowiedzi:


89

Najprostszym sposobem jest skorzystanie z tej binaryopcji. Od :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.

Jeśli tego nie zrobisz, a twoje środowisko korzysta z kodowania wielobajtowego (np. UTF-8, jak większość ludzi używa), Vim próbuje zakodować tekst jako taki, co zwykle prowadzi do uszkodzenia pliku.

Możesz to sprawdzić, otwierając plik i po prostu używając :w. To się teraz zmieniło.
Jeśli ustawisz LANGi LC_ALLna C(ASCII), Vim nic nie konwertuje, a pliki pozostaną takie same (mimo to dodaje nowy wiersz), ponieważ Vim nie będzie musiał wykonywać żadnego kodowania wielobajtowego.

Ja też wolę wyłączyć set wrap binarne, chociaż inni wolą to włączyć . YMMV. Kolejną przydatną rzeczą do zrobienia jest :set display=uhex. Od :help 'display':

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

Na koniec możesz pokazać wartość szesnastkową znaku pod kursorem na linijce za pomocą %B( :set rulerformat=0x%B).

Bardziej zaawansowany: xxd

Możesz użyć tego xxd(1)narzędzia do konwersji pliku do bardziej czytelnego formatu i (to jest ważny bit), przeanalizować edytowany „format czytelny” i zapisać go jako dane binarne. xxdjest częścią vim, więc jeśli vimzainstalowałeś, powinieneś także mieć xxd.

Aby go użyć:

$ xxd /bin/ls | vi -

Lub jeśli już otworzyłeś plik, możesz użyć:

:%!xxd

Teraz dokonaj zmian, musisz to zrobić po lewej stronie wyświetlacza (liczby szesnastkowe), zmiany po prawej stronie (reprezentacja do wydrukowania) są ignorowane podczas zapisu.

Aby go zapisać, użyj xxd -r:

:%!xxd -r > new-ls

Spowoduje to zapisanie pliku w new-ls.

Lub załadować plik binarny do bieżącego bufora:

:%!xxd -r

Od 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.

A potem po prostu użyj, :waby to napisać. ( uwaga : chcesz ustawić binary opcję przed zapisaniem do pliku, z tych samych powodów, które opisano powyżej).

Uzupełniające się skróty klawiszowe, aby to trochę ułatwić:

" 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>

Jest to również dostępne z menu, jeśli używasz gVim, w „Narzędzia ➙ Konwertuj na HEX” i „Narzędzia ➙ Konwertuj z powrotem”.

Wskazówki vim Wiki ma stronę z większą zawartością, a niektóre skrypty pomocnicze. Osobiście uważam, że lepiej jest używać prawdziwego edytora szesnastkowego, jeśli często edytujesz pliki binarne. Vim może w pewnym sensie wykonać to zadanie, ale oczywiście nie jest do tego przeznaczony, a jeśli kiedykolwiek napiszesz bez niego, :set binarymoże zniszczyć twoje pliki binarne!


4
Piękna odpowiedź, ale prawdopodobnie powinna zaczynać się od: „Nie próbuj tego w domu, dzieci!”
msw

Co jeśli muszę usunąć niektóre bajty? np. w środku pliku binarnego.
Anton K

Nie wiem, co robi Vim, ale dodaje 95 KB tekstu do pliku binarnego 200 KB, mimo że nic nie zmieniłem. Nawet z :set binary noeol fenc=utf-8. W rzeczywistości robi to natychmiast po otwarciu pliku, zanim to powie [noeol] [converted]. Dlaczego vim musi zwiększyć bufor o 150%? Jak mogę zapobiec takiemu uszkodzeniu plików?
Braden Best

Jedyne, co działa, to :r !xxd <file>(lub $ xxd <file> | vim -) czytanie i :w !xxd -r > <file>pisanie, ale nie jest to idealne.
Braden Best

Doskonała odpowiedź. Pamiętaj, że adres URL błogosławieństwa nie działa; Znalazłem to (myślę) na github na github.com/bwrsandman/Bless .
sonofagun

19

Aby wyświetlić zawartość pliku binarnego w widoku szesnastkowym, otwórz plik, włącz tryb binarny i przefiltruj bufor za pomocą xxdpolecenia:

:set binary
:%!xxd

Możesz dokonać zmian w lewym obszarze (edytować numery szesnastkowe), a kiedy będziesz gotowy, przefiltruj xxd -ri ostatecznie zapisz plik:

:%!xxd -r
:w

Jeśli krok filtrowania po otwarciu i przed zamknięciem wydaje się uciążliwy, a często robisz to z plikami z .binrozszerzeniem, możesz dodać to do vimrc, aby proces był automatyczny:

" 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

Gdybym tych instrukcji (otwórz plik binarny, :%!xxd, :%!xxd -r, :w, zrobił wprowadzać żadnych zmian!) To plik binarny napisane jest nie taka sama jak oryginał ... Czy to sprawa dla ciebie (I przetestowane /bin/ls). Muszę użyć :set binaryprzed zapisaniem (zobacz także moją odpowiedź, która wyjaśnia dlaczego) ... Może to coś w moim vimrc? Ale niezależnie od tego zawsze używałbym set binarydla bezpieczeństwa ...
Martin Tournoij

1
Zamiast tego możesz dodać augroupskrypt, ~/.vim/plugin/binary.vimjeśli nie chcesz zaśmiecać.vimrc
thom_nic

Jeśli korzystasz z instalacji zagranicznej, ten augroup Binarywpis znajduje się w :help hex-editinglub :help using-xxdw Vimie od 5.5 (wrzesień 1999).
bb010g

6

Użyj edytora „bvi”. http://bvi.sourceforge.net/ (znajduje się w każdym repozytorium 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
Bardziej zaawansowaną alternatywą jest bviplus, który ma kontrolę vima.
Anton K

Strona główna Bviplus i zrzuty ekranu .
Iulian Onofrei

3

TL; DR Odpowiedź

Otwórz plik za pomocą Vima w trybie binarnym:

vim -b <file_to_edit>

W Vimie przejdź do trybu edycji szesnastkowej:

:%!xxd -p

Zapisać:

:%!xxd -p -r
:w

Spowoduje to konwersję bufora z trybu szesnastkowego, a następnie zapisanie pliku w zwykły sposób.

Zwróć uwagę na opcję -p. Pozwala to uniknąć dodatkowego zbędnego drukowania i adresowania puchu, a jedynie pokazuje heks. Po prostu pomiń -p, jeśli potrzebujesz dodatkowego kontekstu.

Ostrożnie otwieraj plik, gdy Vim nie jest w trybie binarnym, ponieważ podczas zapisywania doda (zwykle niezamierzony) znak LF na końcu pliku.


To tak naprawdę nie dodaje niczego, czego nie ma w innych odpowiedziach.
Herb Wolfe

5
Prawdziwy TL; DR jest w środku :h using-xxdi istnieje od tego czasu v7.0001i prawdopodobnie dłużej. Ta witryna byłaby mniej aktywna, gdyby ludzie przeszukiwali dokumenty.
Tommy A

1

To wygląda mało poręczne vim wtyczki, która wykonuje prace przy użyciu pliku tymczasowego, który pisze iz powrotem automatycznie.

Kilka lat temu znalazłem podobną wtyczkę, którą dostosowałem i ulepszyłem na własny użytek. Podałem tutaj odpowiedni kod na wypadek, gdyby ktoś tego chciał. Opiera się również na narzędziu xxd. Jestem pewien, że wersja GitHub, którą podłączyłem powyżej, działa lepiej, ale ja sam jej nie użyłem, więc pomyślałem, że opublikuję również tę, o której wiem, że na pewno działa.

Źródłem tej drugiej wersji była wiki vim, w szczególności ta strona .

Oto kod:

"-------------------------------------------------------------------------------  
" 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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.