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