31

I have folderA that has some files with a number sequence starting with a_000000. What I want to do is to delete files starting from a specific number: let's say a_000750 till the end of files in this folderA. Could anyone please advise how to do this using shell script?

9
  • Can I assume that all of those filenames have 6-digit suffixes?
    – muru
    Commented Sep 27, 2014 at 14:24
  • yes they are :) they start from a_000000 till some number
    – Tak
    Commented Sep 27, 2014 at 14:25
  • 5
    rm a_000[89]* a_0007[5-9]* ?
    – Rinzwind
    Commented Sep 27, 2014 at 14:29
  • @Rinzwind could you please explain this command?
    – Tak
    Commented Sep 27, 2014 at 14:30
  • 2
    @user1460166 a_000[89]* includes every file beginning with a_0008 or a_0009, and a_0007[5-9]* includes every file beginning with a_0007 and then containing a number between 5 and 9, followed by anything.
    – muru
    Commented Sep 27, 2014 at 14:47

5 Answers 5

50

Assuming you know or can guess the end of the range, you could use brace expansions:

rm a_{000750..000850}

The above will delete the 101 files between a_000750 and a_000850 inclusive (and complain about filenames that refer to non-existing files). If you have too many files for that, use find:

find . -name 'a_*' | while read -r file; do 
  [ "${file#./a_}" -gt 000749 ] && rm -v "$file" 
done

Here, the find simply lists all files matching a_*. The list is passed to a while loop where each file name is read into the variable $file. Then, using bash's string manipulation features, if the numerical part (find prints files as ./file, so ${file#./a_} prints the number only) is 000750 or greater, the file is deleted. The -v is just there so you can see what files were removed.

Note that the above assumes sane file names. If your names can have spaces, newlines or other weird characters, use this instead:

find . -name 'a_*' -print0 | while IFS= read -rd '' file; do 
  [ "${file#./a_}" -gt 000749 ] && rm -v "$file" 
done
5
  • Why avoid [[?
    – muru
    Commented Sep 27, 2014 at 15:01
  • 1
    @muru why use it? [[ doesn't simplify things here [[ "${file#./a_}" > 000749 ]] for example, doesn't even make it any shorter. I don't like using unnecessary syntax and this one will even work in simpler shells like dash.
    – terdon
    Commented Sep 27, 2014 at 15:13
  • Because [[ handles spaces and weirdnesses better (i don't care much for > either).
    – muru
    Commented Sep 27, 2014 at 15:13
  • 1
    @muru yes, but [ is fine if you quote the variable as I have, works on far more shells and is simpler.
    – terdon
    Commented Sep 27, 2014 at 15:14
  • If you insist. Not my problem.
    – muru
    Commented Sep 27, 2014 at 15:15
3

You could do something like this:

find . -regextype posix-extended -iregex './a_[0-9]{6}' -execdir bash -c '[[ ${1##./a_} > 000750 ]] && echo $1' "removing: " {} \;

Or:

find . -regextype posix-extended -iregex './a_[0-9]{6}' | sort | sed '0,/000750/d' | xargs echo

The first method assumes a fixed prefix, strips it off and checks the value.

The second method assumes a fixed-length suffix (and a common fixed prefix) and relies on that fact; and that, while 201 comes before 31 in lexicographically, it doesn't before 031.

Test this out with the echo command, and once you're sure it lists the correct files, use rm instead.

6
  • none of them is working :/
    – Tak
    Commented Sep 27, 2014 at 14:43
  • @user1460166 ah, that's probably due the regex matching. I'll update it.
    – muru
    Commented Sep 27, 2014 at 14:47
  • still not working. btw, the files are .png :)
    – Tak
    Commented Sep 27, 2014 at 14:51
  • @user1460166 can you say how it is not working?
    – muru
    Commented Sep 27, 2014 at 14:52
  • I open the terminal, cd to the folder then copy your line and paste it, but the files are not deleted
    – Tak
    Commented Sep 27, 2014 at 14:55
3

In Bash you can run:

for i in {1001..3000} ; do rm file_"$i".dat ; done
0

POSIX shell solution

terdon's first solution relies on brace expansion, which is a property of bash and ksh, however it cannot be used in the standard /bin/sh shell, which on Ubuntu is symlinked to /bin/dash.

In cases where you have to rely /bin/sh for portability of your scripts, there's generally two ways to approach this. One would be via globbing. Just cd folderA and from there run rm a_*. The other way, would be to implement a C-style for loop alternative using while <CONDITION>;do ...done in shell language and format the numbers with printf:

$ sh -c 'i=0;while [ $i -le 750 ]; do filename=$(printf "a_%06d" $i);echo "$filename";i=$((i+1)) ;done'

Notice that here I use echo. Replace echo "$filename" with rm ./"$filename" or rm -- "$filename" when you're ready to delete the files. Also note, that this should be performed when you already cded into the desired directory.

(ab)using awk

Awk being a nice C-like language can help us in two ways: (1) we can generate filenames with for-loop and format them via sprintf function, and (2) delete said files via system() command, which will pass our generated filename and rm command to /bin/sh :

awk 'BEGIN{for(i=0;i<=750;i++){filename=sprintf("a_%06d",i);system("rm "filename);} }'

Perl

Continuing with the idea of portable approach where we "generate" filenames, we can do same in Perl:

perl -le 'for(0..750){$fd=sprintf("a_%06d",$_);unlink($fd)}'
0

As simple as

rm partialfilename* -f

In your example it is

rm a_00075* -f
2
  • 2
    Sorry to say, but that's not a range, those are all files starting with a_00075...
    – Fabby
    Commented Jul 5, 2019 at 18:11
  • 2
    * is a wildcard, OP is looking for an answer on how to delete a RANGE of things... eg if you had files 1 through 100, and wanted to delete #41 to #75 Commented Jul 5, 2019 at 18:48

You must log in to answer this question.

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