Apolônio Serafim
Apolônio Serafim

Reputation: 53

Execute rm with string on file and delete line

I have a file (log.txt) with multiples lines.

Uploaded 1Y3JxCDpjsId_f8C7YAGAjvHHk-y-QVQM at 1.9 MB/s, total 3.9 MB
Uploaded 14v58hwKP457ZF32rwIaUFH216yrp9fAB at 317.3 KB/s, total 2.1 MB

Each line in log.txt represents a file that needs to be deleted. I want to delete the file and then delete the respective line.

Example:

rm 1Y3JxCDpjsId_f8C7YAGAjvHHk-y-QVQM

and after deleting the file that the log.txt contains, delete the line, leaving only the others.

Uploaded 14v58hwKP457ZF32rwIaUFH216yrp9fAB at 317.3 KB/s, total 2.1 MB

Upvotes: 0

Views: 194

Answers (3)

Fravadona
Fravadona

Reputation: 16805

The following code reads log.txt line by line, captures the filename with a bash ERE and tries to delete that file. When the regex or the deletion fails it outputs the original line.

#!/bin/bash

tmpfile=$( mktemp ) || exit 1

while IFS='' read -r line
do
    [[ $line =~ ^Uploaded\ (.*)\ at ]] &&
    rm -- "${BASH_REMATCH[1]}" ||
    echo "$line"
done < log.txt > "$tmpfile" &&
mv "$tmpfile" log.txt

remark: the while loop final result is true unless there's a problem reading log.txt or generating "$tmpfile", so chaining the mv with && makes it so that you won't overwrite the original logfile abusively.

Upvotes: 2

Jetchisel
Jetchisel

Reputation: 7781

Another approach using bash4+ and GNU tools.

#!/usr/bin/env bash

##: Save the file names in an array named files using mapfile aka readarray.
##: Process Substitution and With GNU grep that supports the -P flag.
mapfile -t files < <(grep -Po '(?<=Uploaded ).*(?= at)' log.txt)

##: loop through the files ("${files[@]}") and check if it is existing (-e).
##: If it does, save them in an array named existing_file.
##: Add an additional test if need be, see "help test".
for f in "${files[@]}"; do
  [[ -e $f ]] && existing_file+=("$f")
done

##: Format the array existing_file into a syntax that is accepted
##: by GNU sed, e.g. "/file1|file2|file3|file4/d" and save it
##: in a variable named to_delete.
to_delete=$(IFS='|'; printf '%s' "/${existing_file[*]}/d")

##: delete/remove the existing files.
##: Not sure if ARG_MAX will come up.
echo rm -v -- "${existing_file[@]}"

##: Remove the deleted files (lines that contains the file name)
##: from log.txt using GNU sed.
echo sed -E -i "$to_delete" log.txt

  • Remove all the echo if you're satisfied with the output.

  • This not exactly what you asked for and it is not perfect but it just might be what you need.

Upvotes: 1

Nic3500
Nic3500

Reputation: 8591

Try this:

#!/bin/bash

logfile="logfile.txt"
logfilecopy=$( mktemp )
cp "$logfile" "$logfilecopy"

while IFS= read -r line
do
    filename=$( echo "$line" | sed 's/Uploaded \(.*\) at .*/\1/' )
    if [[ -f "$filename" ]]
    then
        tempfile=$( mktemp )
        rm -f "$filename" && grep -v "$line" "$logfile" >"$tempfile" && mv "$tempfile" "$logfile"
    fi
done < "$logfilecopy"

# Cleanup
rm -f "$logfilecopy"

It does:

  • keep a copy of the original log file.
  • read each line of this copy using while and read.
  • for each line, extract the filename. Note, done with sed since a filename could contain spaces. Therefore cut would not work as required.
  • If the file exists, delete it, remove the line from the log file and store it in a temporary file, move the temporary file into the log file.
  • that last step is done with && between commands to ensure that the last command is done before continuing. If the rm fails, the log entry must not be deleted.
  • finally delete the original log file copy.
  • you can add echo statements and-or -x to $!/bin/bash to debug if required.

Upvotes: 3

Related Questions