254

I use md5sum to generate a hash value for a file. But I only need to receive the hash value, not the file name.

md5=`md5sum ${my_iso_file}`
echo ${md5}

Output:

3abb17b66815bc7946cefe727737d295  ./iso/somefile.iso

How can I 'strip' the file name and only retain the value?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
John Doe
  • 9,843
  • 13
  • 42
  • 73

17 Answers17

255

A simple array assignment works... Note that the first element of a Bash array can be addressed by just the name without the [0] index, i.e., $md5 contains only the 32 characters of md5sum.

md5=($(md5sum file))
echo $md5
# 53c8fdfcbb60cf8e1a1ee90601cc8fe2
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Peter.O
  • 6,696
  • 4
  • 30
  • 37
  • Awesome. Just one question, I know the question is tagged `bash`, but can you tell me if array is a bash only feature or some shell standard? – jyz Nov 05 '14 at 10:37
  • 2
    the first line doesn't work inside the do section of a for loop...as a Bash newb I don't yet know why – Andy Jul 29 '15 at 01:20
  • 1
    @Andy: If you try this line of code (in the terminal, or in a script): `echo>file; for i in file; do md5=($(md5sum file)); echo $md5; done` - It should output `68b329da9893e34099c7d8ad5cb9c940` – Peter.O Jul 29 '15 at 08:35
  • @Peter.O that does, I figured out that the problem was I had `#!/bin/sh` at the top of my script instead of `#!/bin/bash`. Thanks! – Andy Jul 29 '15 at 15:18
  • 1
    How come `echo ($(echo -n foo | md5sum))` doesn't work? Errors out `bash: syntax error near unexpected token $(echo -n foo | md5sum)'` – lkraav Aug 26 '15 at 04:42
  • 3
    @lkraav: The *command* `echo -n foo | md5sum` outputs 2 shell *words* to *stdout*: `acbd18db4cc2f85cedef654fccc4a4d8` and `-` (the `-` indicates the source as *stdin*). – You must tell *bash* to capture those *words* into a string, using *Command Substitution*: `$( command )`. – The `(` brackets `)` produce a *bash array* with 2 elements. However, you must assign that array construct `( … )` to an variable name; hence, using *md5* as the array name: `md5=($(echo -n foo | md5sum))`. You haven't assigned the array to a variable name – Peter.O Aug 26 '15 at 07:04
  • 4
    This is a bash question, but note that this doesn't work in zsh. Instead you can `echo $md5[1]` to get only the hash (but this isn't portable to bash)... – Shane Mar 08 '16 at 09:04
  • SO: Searching how to create a md5 hash of a file, learning the shortcut access of the first array element. Thanks :) – mgutt Aug 12 '19 at 18:51
  • 1
    To make the script a bit more bulletproof make sure to set `IFS` explicitly in case it is set to some non-standard value: `IFS=" " md5=($(md5sum file))` – Stepan Kolesnik Jun 12 '20 at 19:20
  • This is a nice solution, but it doesn't work in Jenkins – scrutari Oct 14 '20 at 10:08
226

Using AWK:

md5=`md5sum ${my_iso_file} | awk '{ print $1 }'`
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jyz
  • 6,011
  • 3
  • 29
  • 37
  • Wrong it gives following output on Mac `MD5 (/Users/hello.txt) = 24811012be8faa36c8f487bbaaadeb71` and your code returns `MD5`. – alper Aug 03 '18 at 21:06
  • You can get run of `-` by adding `| awk '{print $1}'` end of your code => `md5sum < ${my_iso_file} | awk '{print $1}'` @ChristopheDeTroyer – alper Aug 03 '18 at 21:11
84

You can use cut to split the line on spaces and return only the first such field:

md5=$(md5sum "$my_iso_file" | cut -d ' ' -f 1)
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • 7
    @CzarekTomczak True, but just by using this answer's method, you could reuse it with different hashing algorithms just by changing the command itself. `md5sum` -> `sha256sum` without remembering what amount of characters you need to "cut". – David Tabernero M. Aug 15 '18 at 01:05
32

On Mac OS X:

md5 -q file
trevor
  • 345
  • 2
  • 2
17
md5="$(md5sum "${my_iso_file}")"
md5="${md5%% *}" # remove the first space and everything after it
echo "${md5}"
Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • Nice. One note -- on the first line you don't need quotes around `$()` (although they do no harm) but certainly need them around `${}`. – Roman Cheplyaka Sep 13 '10 at 20:26
  • 1
    @Roman: yeah, I tend to habitually quote any expansion (unless there's a reason not to) -- it's easier than keeping track of the cases where it's safe to skip the quotes. (Although in this case, I left them off the actual filename... stand by for an edit.) – Gordon Davisson Sep 14 '10 at 06:31
14

Another way is to do:

md5sum filename | cut -f 1 -d " "

cut will split the line to each space and return only the first field.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
azerttyu
  • 175
  • 1
  • 2
7

By leaning on head:

md5_for_file=`md5sum ${my_iso_file}|head -c 32`
Vladimir Djuricic
  • 4,323
  • 1
  • 21
  • 22
6

One way:

set -- $(md5sum $file)
md5=$1

Another way:

md5=$(md5sum $file | while read sum file; do echo $sum; done)

Another way:

md5=$(set -- $(md5sum $file); echo $1)

(Do not try that with backticks unless you're very brave and very good with backslashes.)

The advantage of these solutions over other solutions is that they only invoke md5sum and the shell, rather than other programs such as awk or sed. Whether that actually matters is then a separate question; you'd probably be hard pressed to notice the difference.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • The question is tagged with Bash, but perhaps indicate how much of this is Bash-specific (e.g., would it work in [Z shell](https://en.wikipedia.org/wiki/Z_shell) (now allegedly the ***default shell*** in [macOS v10.15](https://en.wikipedia.org/wiki/MacOS_Catalina) (Catalina) and later)). – Peter Mortensen Apr 19 '21 at 15:46
6

If you need to print it and don't need a newline, you can use:

printf $(md5sum filename)
Alex
  • 69
  • 1
  • 2
3
md5=$(md5sum < $file | tr -d ' -')
pixelbeat
  • 30,615
  • 9
  • 51
  • 60
2
md5=`md5sum ${my_iso_file} | cut -b-32`
pixelbeat
  • 30,615
  • 9
  • 51
  • 60
letronje
  • 9,002
  • 9
  • 45
  • 53
1

md5sum puts a backslash before the hash if there is a backslash in the file name. The first 32 characters or anything before the first space may not be a proper hash.

It will not happen when using standard input (file name will be just -), so pixelbeat's answer will work, but many others will require adding something like | tail -c 32.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Wojciech
  • 11
  • 2
1

if you're concerned about screwy filenames :

md5sum < "${file_name}" | awk NF=1   
f244e67ca3e71fff91cdf9b8bd3aa7a5

other messier ways to deal with this :

md5sum "${file_name}" | awk NF=NF OFS= FS=' .*$' 
                     or
                      | awk '_{ exit }++_' RS=' '    
f244e67ca3e71fff91cdf9b8bd3aa7a5

to do it entirely inside awk :

mawk 'BEGIN {
    __ = ARGV[ --ARGC ]
     _ = sprintf("%c",(_+=(_^=_<_)+_)^_+_*++_) 
    RS = FS
    gsub(_,"&\\\\&",__)

    ( _=" md5sum < "((_)(__)_) ) | getline

    print $(_*close(_)) }' "${file_name}"
f244e67ca3e71fff91cdf9b8bd3aa7a5
RARE Kpop Manifesto
  • 2,453
  • 3
  • 11
0

Well, I had the same problem today, but I was trying to get the file MD5 hash when running the find command.

I got the most voted question and wrapped it in a function called md5 to run in the find command. The mission for me was to calculate the hash for all files in a folder and output it as hash:filename.

md5() { md5sum $1 | awk '{ printf "%s",$1 }'; }
export -f md5
find -type f -exec bash -c 'md5 "$0"' {} \; -exec echo -n ':' \; -print

So, I'd got some pieces from here and also from 'find -exec' a shell function in Linux

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fabio Montefuscolo
  • 2,258
  • 2
  • 21
  • 18
0

For the sake of completeness, a way with sed using a regular expression and a capture group:

md5=$(md5sum "${my_iso_file}" | sed -r 's:\\*([^ ]*).*:\1:')

The regular expression is capturing everything in a group until a space is reached. To get a capture group working, you need to capture everything in sed.

(More about sed and capture groups here: How can I output only captured groups with sed?)

As delimiter in sed, I use colons because they are not valid in file paths and I don't have to escape the slashes in the filepath.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
FleMo
  • 539
  • 6
  • 20
-3

Another way:

md5=$(md5sum ${my_iso_file} | sed '/ .*//' )
codaddict
  • 445,704
  • 82
  • 492
  • 529
-3
md5=$(md5sum < index.html | head -c -4)
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439