4

I'm trying to make one command that will take all drives that are not sda and run shred on them and dump the output to a log file. So far I have

find /dev/ -name "sd?" -not -name "sda" -exec [shred -fvz {} > /home/ben/ProjectsInProgress/ShredLogs/$(hdparm -I {} | grep 'Serial\ Number' |cut -f2- -d:).log 2>&1 &] \;

As you can see, I have gotten a fair distance into this with searching google, but now i ran into something I don't know what to do because the output I got is

[1] 13097
{}: No such file or directory
]: command not found
[1]+  Exit 1                  find /dev/ -name "sd?" -not -name "sda" -exec [shred -fvz {} > /home/ben/ProjectsInProgress/ShredLogs/$(hdparm -I {} | grep 'Serial\ Number' |cut -f2- -d:).log 2>&1

Does anyone have a suggestion?

Also, I am currently working on this problem but suggestions would be nice. When I run

shred -fvz /dev/sdb > /home/ben/ProjectsInProgress/ShredLogs/$(hdparm -I /dev/sdb | grep 'Serial\ Number' |cut -f2- -d:).log 2>&1

I get an ambiguous redirect error.

Byte Commander
  • 110,243

2 Answers2

2

This script should do what you want. Note that it must run as root!

#!/bin/bash
logpath="/home/ben/ProjectsInProgress/ShredLogs"
lsblk -l | grep -o '^sd[b-z] ' | while read f; do
    logfile="$logpath/$(hdparm -i "/dev/$f" | grep -oP "(?<=SerialNo=)\w+")"
    shred -fvz "$f" &> "$logfile"
done

Thanks to @terdon for providing this improved (and hopefully now fixed) version.

Please tell me whether it works, I'm not really motivated to test shredding my own disks...

Byte Commander
  • 110,243
2

EDIT: Though the original answer tells you part of why your find command didn't work, the loop using find won't work for USB HDDs. Scroll to the bottom of the answer for a better version.

Note that the logging won't work at all with USB flash drives; hdparm is only for HDDs.


Your find command won't work. At all. Why?

There are a few problems, but the major one is this: The -exec option looks in the PATH for an executable with the name of its first argument and executes it with any other arguments to find up to a semicolon. It does not pass its arguments to a shell, which means using $(...), redirection, etc will not work.

How do you fix this? There are a few ways.

The most obvious might be to pass your command to a shell:

find . -name testing12345 -exec bash -c '...$(...)... > /path/to/some/file 2>&1' \;

Yes, this works, but in a complicated expression with multiple types of quotes, escaping can quickly become a nightmare.

Often, using a loop is the easiest way to do this. Since you already have a working find command, the easiest way to do this is to take the -exec arguments off of it and pipe it into a loop:

find (your find arguments here) -print0 | while IFS= read -r -d $'\0' filename; do
    # process "$filename"
done

IFS= and the -print0 and -d $'\0' options to find and read avoid problems with newlines in file names. The -r option ensures that read doesn't process backslashes in the file names.

Combining that and fixes to your other problems, we get this, which seems to work:

find /dev/ -name "sd?" -not -name "sda" -print0 | while IFS= read -r -d $'\0' filename; do
    if (hdparm -i "$filename" > /dev/null 2>&1); then
        shred -fvz "$filename" > /home/ben/ProjectsInProgress/ShredLogs/$(hdparm -i "$filename" | grep -o 'SerialNo=\S*' |cut -f2 -d=).log 2>&1 &
    fi
done

I've only tested it myself with an echo statement in place of shred since I don't want to shred my disks, so please tell me if it works.


EDIT: The find version is broken for USB HDDs. This modified version that uses lsblk works correctly:

lsblk -ld | grep -o '^sd[b-z]' | while read -r filename; do
    filename="/dev/$filename"
    shred -fvz "$filename" > "/home/ben/ProjectsInProgress/ShredLogs/$(hdparm -I "$filename" | grep -o 'Serial Number:\s*\S*' | cut -f2 -d: | sed 's/^\s*//').log" 2>&1 &
done