Hexdump de uma string começando em novas linhas?

5

Digamos que eu tenha várias cadeias de caracteres, mas as entradas nela são curtas; se eu tentar hexdump, então eu recebo algo assim:

echo "something
is
being
written
here" | hexdump -C

#00000000  73 6f 6d 65 74 68 69 6e  67 0a 69 73 0a 62 65 69  |something.is.bei|
#00000010  6e 67 0a 77 72 69 74 74  65 6e 0a 68 65 72 65 0a  |ng.written.here.|
#00000020

A maioria dos programas de despejo hexadecimal, incluindo hexdump , simplesmente funcionam como uma matriz 2D (você pode definir quantos bytes / coluna você terá por linha); e assim, neste caso, toda a saída é compactada em duas linhas de despejo.

Existe um programa que eu possa usar, o qual continuaria como de costume - exceto quando ele encontra uma nova linha ( 0x0a - mas possivelmente qualquer outro caractere, ou sua sequência), ele também iniciaria uma nova linha? Nesse caso, imagino uma saída como:

00000000  73 6f 6d 65 74 68 69 6e  67 0a                    |something.|
0000000a  69 73 0a                                          |is.|
0000000d  62 65 69 6e 67 0a                                 |being.|
00000013  77 72 69 74 74 65 6e 0a                           |written.|
0000001b  68 65 72 65 0a                                    |here.|
00000020
    
por sdaau 14.03.2015 / 18:22

3 respostas

0

Bem, há printf ...

hex_split()(    unset c dump slice rad pend
        _get(){ dd bs=1024 count=1; echo .; } 2>/dev/null
        _buf()  case $((${#dump}>0)):$((${#slice}>0)) in
                (0:*)   dump=$(_get); dump=${dump%.}
                        [ -n "$dump" ] || [ -n "$slice" ];;
                (*:0)   [ "${#dump}" -lt 16 ]       &&
                        slice=${dump:-$slice} dump= && return
                        slice=${dump%"${dump#$q}"} dump=${dump#$q};;esac
        _out(){ printf "%08x%02.0s" "$rad" "$((rad+=$#/2))"
                printf "%02x %.0s" "$@"
                printf "%-$(((16-($#/2))*3))s"
                printf "%.0s%.1s" '' ' ' '' \| "$@" '' \| '' "$nl"
};      q=$(printf %016s|tr \  \?) ; IFS=\  nl='
'       rad=0 c=0 split=${split:-$nl} slice="$*"; set --
        while   [ -n "$slice" ] || _buf || ! ${1:+"_out"} "$@" &&
                c=${slice%"${slice#?}"} slice=${slice#?}                
        do      set "$@" "'$c" "${c#[![:print:]]}."
                case $#$c in    (32*|*$split)   _out "$@"; set --;;esac
        done
)

Você pode entregar stdin ou argumentos ou ambos. Então ...

echo "something
is
being
written
here" | hex_split something else besides

... as impressões acima ...

00000000  73 6f 6d 65 74 68 69 6e 67 20 65 6c 73 65 20 62  |something else b|
00000010  65 73 69 64 65 73 00 73 6f 6d 65 74 68 69 6e 67  |esides.something|
00000020  0a                                               |.|
00000021  69 73 0a                                         |is.|
00000024  62 65 69 6e 67 0a                                |being.|
0000002a  77 72 69 74 74 65 6e 0a                          |written.|
00000032  68 65 72 65 0a                                   |here.|

Altere o caractere de divisão padrão como ...

split=${somechar} hex_split
    
por 14.03.2015 / 20:53
0

Aqui está uma possibilidade, uma solução compacta que está fazendo uso da capacidade de read de restringir a quantidade de caracteres de leitura:

c=0
while IFS= read -n16 -r line
do
  len=${#line}
  ((len<16)) && { ((len++)) ; line+=$'\n' ;}
  printf "%08x  " $c
  for ((i=0; i<len; i++))
  do  printf " %02x" "'${line:i:1}"
  done
  printf " %*s %s\n" $((50-3*len)) "" "'${line//[^[:print:]]/.}'"
  ((c+=len))
done
    
por 15.03.2015 / 13:27
0

Eu precisava disso para comparar dois arquivos com um difftool, mas ainda ser capaz de ver que tipo de caracteres não imprimíveis diferem.

Esta função adiciona uma opção -n a hexdump . Se -n for especificado, a saída será dividida em quebras de linha, se o hexdump normal não for chamado. Em comparação com a resposta do @Janis esta não é uma reescrita completa do hexdump, mas em vez disso o hexdump é chamado com os outros parâmetros especificados se dado. Mas o hexdump é alimentado pela entrada usando a opção head e -s skip para preservar os deslocamentos. A função funciona ao ser canalizada, bem como quando o arquivo é especificado. Embora não funcione para vários arquivos especificados, como o hexdump faria.

Eu queria tornar isso uma resposta alternativa mais fácil / mais curta, mas a proteção contra todos esses casos de margem para insumos realmente tornou isso mais longo.

hexdump()
{
    # introduces artifical line breaks in hexdump output at newline characters
    # might be useful for comparing files linewise, but still be able to
    # see the differences in non-printable characters utilizing hexdump
    # first argument must be -n else normal hexdump will be used
    local isTmpFile=0
    if [ "$1" != '-n' ]; then command hexdump "$@"; else
        if [ -p /dev/stdin ]; then
            local file="$( mktemp )" args=( "${@:2}" )
            isTmpFile=1
            cat > "$file" # save pipe to temporary file
        else
            local file="${@: -1}" args=( "${@:2:$#-2}" )
        fi
        # sed doesn't seem to work on file descripts for some very weird reason,
        # the linelength will always be zero, so check for that, too ...
        local readfile="$( readlink -- "$file" )"
        if [ -n "$readfile" ]; then 
            # e.g. readlink might return pipe:[123456]
            if [ "${readfile::1}" != '/' ]; then 
                readfile="$( mktemp )"
                isTmpFile=1
                cat "$file" > "$readfile"
                file="$readfile"
            else
                file="$readfile"
            fi
        fi
        # we can't use read here else \x00 in the file gets ignored.
        # Plus read will ignore the last line if it does not have a \n!
        # Unfortunately using sed '<linenumbeer>p' prints an additional \n
        # on the last line, if it wasn't there, but I guess still better than
        # ignoring it ...
        local linelength offset nBytes="$( cat "$file" | wc -c )" line=1
        for (( offset = 0; offset < nBytes; )); do
            linelength=$( sed -n "$line{p;q}" -- "$file" | wc -c )
            (( ++line ))
            head -c $(( offset + $linelength )) -- "$file" | 
            command hexdump -s $offset "${args[@]}" | sed '$d'
            (( offset += $linelength ))
        done
        # Hexdump displays a last empty line by default showing the
        # file size, bute we delete this line in the loop using sed
        # Now insert this last empty line by letting hexdump skip all input
        head -c $offset -- "$file" | command hexdump -s $offset "$args"
        if [ "$isTmpFile" -eq 1 ]; then rm "$file"; fi
    fi
}

Você pode experimentar com echo -e "test\nbbb\nomg\n" | hexdump -n -C , que imprime:

00000000  74 65 73 74 0a                                    |test.|
00000005  62 62 62 0a                                       |bbb.|
00000009  6f 6d 67 0a                                       |omg.|
0000000d  0a                                                |.|
0000000e

Como bônus, aqui está minha função hexdiff :

hexdiff()
{
    # compares two files linewise in their hexadecimal representation
    # create temporary files, because else the two 'hexdump -n' calls
    # get executed multiple times alternatingly when using named pipes:
    # colordiff <( hexdump -n -C "${@: -2:1}" ) <( hexdump -n -C "${@: -1:1}" )
    local a="$( mktemp )" b="$( mktemp )"
    hexdump -n -C "${@: -2:1}" | sed -r 's|^[0-9a-f]+[ \t]*||;' > "$a"
    hexdump -n -C "${@: -1:1}" | sed -r 's|^[0-9a-f]+[ \t]*||;' > "$b"
    colordiff "$a" "$b"
    rm "$a" "$b"
}

Por exemplo teste com hexdiff <( printf "test\nbbb\x00 \nomg\nbar" ) <( printf "test\nbbb\nomg\nfoo" ) , que será impresso:

2c2
< 62 62 62 11 20 0a                                 |bbb. .|
---
> 62 62 62 0a                                       |bbb.|
4,5c4,5
< 62 61 72                                          |bar|
< 00000012
---
> 0c 6f 6f                                          |.oo|
> 00000010

Editar: Ok, esta função não é adequada para arquivos maiores como 8MB e ferramentas como comparehex ou dhex também não são boas o suficiente, porque ignoram novas linhas e, portanto, não são capazes de combinar as diferenças muito bem. Usar uma combinação de od e sed é muito mais rápido:

hexlinedump()
{
    local nChars=$1 file=$2
    paste -d$'\n' -- <( od -w$( cat -- "$file" | wc -c ) -tx1 -v -An -- "$file" |
        sed 's| 0a| 0a\n|g' | sed -r 's|(.{'"$(( 3*nChars ))"'})|\n|g' |
        sed '/^ *$/d' ) <(
    # need to delete empty lines, because 0a might be at the end of a char
    # boundary, so that not only 0a, but also the character limit introduces
    # a line break
    sed -r 's|(.{'"$nChars"'})|\n|g' -- "$file" | sed -r 's|(.)|  |g' )
}

hexdiff()
{
    colordiff <( hexlinedump 16 "${@: -2:1}" ) <( hexlinedump 16 "${@: -1:1}" )
}
    
por 20.01.2018 / 17:07