19
# dd if=2013-Aug-uptime.csv bs=1 count=1 skip=3 2> /dev/null
d
# dd if=2013-Aug-uptime.csv bs=1 count=1 skip=0x3 2> /dev/null
f

Why the second command outputs a different value?

Is it possible to pass the skip|seek offset to dd as an hexadecimal value?

2 Answers 2

29

Why the second command outputs a different value?

For historical reasons, dd considers x to be a multiplication operator. So 0x3 is evaluated to be 0.

Is it possible to pass the skip|seek offset to dd as an hexadecimal value?

Not directly, as far as I know. As well as multiplication using the operator x, you can suffix any number with b to mean "multiply by 512" (0x200) and with K to mean "multiply by 1024" (0x400). With GNU dd you can also use suffixes M, G, T, P, E, Z and Y to mean multiply by 2 to the power of 20, 30, 40, 50, 60, 70, 80 or 90, respectively, and you can use upper or lower case except for the b suffix. (There are many other possible suffixes. For example, EB means "multiply by 1018" and PiB means "multiply by 250". See info coreutils "block size" for more information, if you have a GNU installation.)

You might find the above arcane, anachronistic, and geeky to the point of absurdity. Not to worry: you are not alone. Fortunately, you can just ignore it all and use your shell's arithmetic substitution instead (bash and other Posix compliant shells will work, as well as some non-Posix shells). The shell does understand hexadecimal numbers, and it allows a full range of arithmetic operators written in the normal way. You just need to surround the expression with $((...)):

# dd if=2013-Aug-uptime.csv bs=1 count=$((0x2B * 1024)) skip=$((0x37))
7
  • 1
    Note that $((...)) is POSIX arithmetic expansion, it is not bash specific at all, you don't have to use bash to use it, any POSIX shell will do. However note that in many shells including bash, it undergoes word splitting so should be quoted. Commented Aug 5, 2013 at 7:08
  • @StephaneChazelas: That's true, it's not bash specific. However, it doesn't need quotes. (Posix: "The expression shall be treated as if it were in double-quotes, except that a double-quote inside the expression is not treated specially.") If there were a variable in the expression, and its value included a separator, then it wouldn't be a valid number; putting quotes around the $((...)) won't change anything. The only case I can think of where quotes would do anything is if you had something like IFS=4 but that would cause all sorts of other chaos.
    – rici
    Commented Aug 6, 2013 at 0:00
  • The section you quote is about what's inside $((...)). POSIX is clear that the expansion of $((...)) is subject to word splitting. Leaving any of the command/arithmetic/variable expansions unquoted in list context is the split+glob operator. Setting IFS=4 would not cause all sorts of problems if you don't use the split+glob operator where it's not needed/wanted, and set IFS every time you need that split+glob operator. Commented Aug 6, 2013 at 18:07
  • @StephaneChazelas: yes, the numeric result is subject to word-splitting. But it's an integer. If you set IFS to something which includes digits, something I don't believe I've ever done, then you would need to quote. Presumably, if one were doing that, one would be aware of the need, since it is hardly something one is likely to do casually. Or, at least, it's hardly something I'm likely to do, period. I don't mean to speak for you.
    – rici
    Commented Aug 6, 2013 at 18:43
  • 1
    @ogurets: what shell are you using? (Please include version).
    – rici
    Commented Oct 21, 2019 at 5:55
1

I know this is an old thread, but the issue is still as revlevant as ever, espcially when idiots like me go and inadvertantly recall and excute a 'cp fileA fileB' from bash history when fileB is not yet checked-in to git not backed up, and containing several hours of coding after an all-nighter :-/

Thanks to the concepts in this thread I was able to fully recover my lost file, however I lost mine on a remote Virtual Private Server with a 32Gb disk and very little RAM (running Ubuntu 18.04), and all my attemtps using 'grep' as above would quickly die with 'insufficient memory'

In my case it was hexdump -C /dev/sdX1 | grep 'shortString' which came to my rescue. Unlike grep, it only displays a very narroy ASCII representation of the hex, so it's vital to only look for a short unique string, and bear in mind that even that could be wrapped. Once it had output some hex address where there was a match, I was able to use 'dd' in a similar fashion as above, except that I found it seemed to be defaulting to a block size of 4096, so I not only had to convert the hex bytes address to decimal but divide it by 4096 to scale it to 4k blocks for dd's skip parameter - unhelpfully, if this number is too large for dd the error message looks like it's complaining about skip= being invalid rather than the number passed to it.

Kudos to the folks who added tips about using bash with $((0xabcd)) to easilty obtaion hex->dec conversion :-)

Just my final word - In my case there were several copies of the same file which were all in close proximity. But subtracting the lowest address from the highest address reported by hexdump I was able to identife an ~5MB region containing all possible copies, which meant I could target dd at the lowest adress and extract that whole region to a temp file. Vim editor now handles binary content fairly gracefully so you can then examine the tempfile and reshape it as needed.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .