Strip directory from filename in checksum output

Viewed 199

I have the output of a checksum used in a unix shell script, and I need only the checksum value and the filename to be displayed.

$ Cksum path/path2/f1.txt | awk '{print $1,$2}'
1237668 path/path2/f1.txt 

However I want the filename without the directory:

1237668 f1.txt 

I have tried sed by which I only get the filename and not the checksum:

$ Cksum path/path2/f1.txt | sed 's/.*path2//'
/f1.txt
6 Answers

Assuming your filenames don't contain spaces, here are are sed and awk solution:

A simpler sed:

cksum path/to/f.txt | sed 's/ .*[/ ]/ /'

878395353 f.txt

This sed starts match from space character and matches until it gets last / or space since .* is greedy. We just replace this matched text with a single space.


Or a simpler awk using / or space as input field separator:

cksum path/to/f.txt | awk -F '[ /]' '{print $1, $NF}'

878395353 f.txt

Based on man cksum

The cksum utility writes to the standard output three whitespace separated fields for each input file. These fields are a checksum CRC, the total number of octets in the file and the file name.

Using sed you could use 3 capture groups and use group 1 and 3 in the replacement.

([^[:space:]]+) [^[:space:]]+ (.*/)?([^[:space:]]+)

Explanation

  • ([^[:space:]]+) Group 1, match 1+ non whitespace chars
  • [^[:space:]]+ Match 1+ chars other than a whitespace char between spaces
  • (.*/)? Optionally match group 2 matching until the last occurrence of /
  • ([^[:space:]]+) Group 3, match 1+ non whitespace chars

For example:

cksum ./file.txt # --> 3777026118 8 ./file.txt

Using sed

cksum ./file.txt | sed -E 's~([^[:space:]]+) [^[:space:]]+ (.*/)?([^[:space:]]+)~\1 \3~' 

Output

3777026118 file.txt

Using awk printing the first field and the last item from the result of splitting the 3rd field:

cksum ./file.txt | awk '
{
  n=split($3,a,"/")
  print $1, a[n]
}'

Output

3777026118 file.txt

Note: I do not know the Cksum command you use. My cksum outputs 3 fields: checksum, size and filename. Adapt the indexes in the following if yours behaves differently.

If your shell is bash, you could use a bash array and basename. If you don't have spaces in your filenames:

$ a=($(cksum path/path2/f1.txt))
$ printf '%s %s\n' "${a[0]}" "$(basename ${a[2]})"
857691210 f1.txt

If you have spaces in your filenames, adapt the printf parameters:

$ a=($(cksum "path/path2/f 1.txt"))
$ printf '%s %s\n' "${a[0]}" "$(basename "${a[*]:2}")"
857691210 f 1.txt

And if you prefer quoting the filename:

$ printf '%s "%s"\n' "${a[0]}" "$(basename "${a[*]:2}")"
857691210 "f 1.txt"

You can also use:

cd $(dirname path/path2/f1.txt); cksum $(basename path/path2/f1.txt)

or, to keep the current directory the same:

a=$(pwd); cd $(dirname path/path2/f1.txt); cksum $(basename path/path2/f1.txt); cd $a

Assuming the / character only occurs in field #2, delete everything after field #2; then remove the directory name:

 Cksum path/path2/f1.txt | 
 sed 's# [^/]*$##;s# .*/# #'
Related