As the title says, I just started with linux mint and am falling in love with bash scripts 😍 Actually I’m not sure if it’s considered a script, but I want to delete the last 2 files in all subfolders in a folder. So far I’ve (after great effort) got the terminal to list the files, but I want to delete them. Here is how I get them listed:
for f in *; do ls $f | tail -n 2; done
All their names come satisfyingly up in the terminal. Now what? I tried adding | xargs rm but that didn’t delete them. I also tried something with find command but that didn’t work either. Some folders have 3 items, so I want to delete #2 and 3. Some folders have 15 items so I want to delete #14 and 15. Folders are arranged by name, so it’s always the last 2 that I want to delete.
It’s frustrating to be sooooo clooooose, but also very fun. Any help is appreciated!
EDIT: Thanks for the awesome help guys! The next part of this is to move all the .html files into one folder (named “done”), prepending their name with an integer. So far I got:
n=1; for f in *; do find ./"$f" -type f | sort | xargs mv done/"$n$f"; n=$((n+1)); done
but that is… not really doing anything. The closest I have gotten so far is some error like
mv: Missing destination file operand
Any help is again appreciated!
Just fyi, if you add a second question that you want people to see, you might avoid adding it as an edit and just post a new question instead or ideally include it all in the first post to begin with.
Out of curiosity, what did you end up using for the first part? I know how I would have done it, but I’m self taught and always interested in learning new/different ways to do things.
For the bonus EDIT question, are you moving all html files from any subdirectory under your current directory? If so, that’s much easier, but I would avoid putting your
done
folder under the scope that you’re scanning against as well as ensuring no files have matching names to avoid overwriting files already moved.All in all, I’m sure you can get there, but it does also help to have more information up front so we can provide clearer help.
Ok, thanks for the tip. I’m still getting used to Lemmy.
I ended up using
for f in *; do find ./“$f” -type f | sort | tail -n 2 | xargs -n 1 rm; done
and it worked perfectly. For the bonus question, I’m moving the html files from 127 subfolders. They are the only content of the subfolders. I want to prepend an integer to each and copy them to a different folder, so instead of
- folder1/file1
- folder1/file2
- folder2/file1
- folder2/file2
- folder2/file3
I’ll have
- 001file1
- 002file2
- 003file1
- 004file2
- 005file3
be careful using
rm
in a loop and/or with variable arguments, things can go very wrong :)when i’m writing a complicated command line involving
rm
i often write and run it first withecho
in place ofrm
just to be sure i am getting the results i expect. also when i re-run it actually usingrm
, i tend to use the-v
option (which tellsrm
to print what it is doing) to reassure myself that i’ve just deleted what i wanted to and nothing else.Great tip, thank you!
ITT: I find out I may not be the power user I think I am.
Haha. I used to wite long and intricate BATCH files for MS DOS to automate all sorts of shit back in the 1990s. Bash is more powerful but much the same thing.
I’ve written a few tiny ones but I’m too old for that shit these days :)
My work is all windows based, I only put on the Linux hat while I’m at home. Powershell is fun when it’s not broken and my commands don’t shit the bed
I’ve been “daily driving” Linux for over 15 years, and I’ve learned a lot about configuration files, but I’ve never learned how to program anything. Couldn’t write a “for loop” to save my life.
I think config files, yaml, and xml are reasonable pieces of code. I think program scripting is absolutely unreasonable fluff.
xargs rm -rf
?for f in *; do ls $f | tail -n 2 | xargs rm -rf; done
You mean like that? rm -rf followed by a question mark does not inspire confidence XD
Additionally, for safety you can add the
i
flag to be promoted to confirm each removal. It may be tedious depending on the number of files, but it may also save you from deleting files and/or directories you don’t want deleted.For clarity, be careful with that
-rf
combo of flags. As another commenter mentioned,-r
means recursive, which will delete directories and their contents. You’re talking about deleting files. If you do not want directories and their contents removed, DO NOT use the-r
flag.Thank you for the tips, but now I’m getting “Cannot remove: No such file or directory” all the way down! The files are there, I see them, they come up in the terminal, but for some reason xargs rm does not want to delete them. When I put the -f flag, rm doesn’t give an error but the files are still there! wtf
When you run the command without the
xargs
bit, like this:for f in \*; do ls $f | tail -n 2; done\
,Does the output give you the full file path, or just the file names?
The full file path will look something like:
/dir1/dir2/actual-file
And of course the file name would just be:
some-file
If you’re getting just the file name, that’s the problem. Unless you’re in the directory with the file you wish to delete,
rm
needs the full path.Edit: grammar
Yea that must be it! It’s spitting out just the file name and not the whole path. There is only 1 level of depth, so I want to remove
- ./folder1/file 3
- ./folder1/file4
- ./folder2/file11
- ./folder2/file12
so how do I get the whole path into xargs? I tried
xargs "$f"/
but fortunately that didn’t work because it was trying to delete all the directories lmao XDHere’s the command to delete the files:
for f in *; do find ./"$f" -type f | sort | tail -n 2 | xargs -n 1 rm; done
If you want to insure it will target the correct files, first run this command (I HIGHLY recommend you do this first. Verify BEFORE you delete so you don’t lose data):
for f in *; do find ./"$f" -type f | sort | tail -n 2; done
I’ll be adding another comment reply with a breakdown of the command shortly (just need to write it up)
Here’s what’s happening in the command;
for f in *; do
You already know this for loop, which is using the
*
glob to iterate over each directory in the current directory.find ./"$f" -type f
Instead of your original
ls
command, which gives the file names, and not their full paths, we’re using GNUfind
, which outputs the full path of what it finds. The arguments are:./"$f"
- This tellsfind
where to start its search. I double qouted thevariable to properly expand the directory name even if it has nonstandard characters in it like spaces.
-type f
- This tellsfind
what kind of file object to look for. So it’s two parts.-type
to tellfind
there will be a specific type to look for, and thef
flag, which means file. Meaning, it will only find filesThe output of find is not sorted alaphabetically, so before piping the output to
tail
, we first pipe it tosort
, which by default will sort alphanumerically, which we then pipe totail
to grab just the last two files, and finally we get to thexargs
bit.Here I added the
-n 1
argument toxargs
to get it to work on the files one at a time. This isn’t actually necessary. You could just run it asxargs rm
. I didn’t realize that before I posted the command. (I’m still learning too! The learning never ends. :D )
deleted by creator
yes. that’s what I suggested… the question mark was there to ask you if you tried that :-D I’m at work, pretty busy :-D I hope you read the
rm
manual.-r
means recursive
-f
means force, which will delete the files/directories without interactionOh I see, lol. Now I’m getting “Cannot remove: No such file or directory” all the way down! The files are there, I see them, they come up in the terminal, but for some reason xargs rm does not want to delete them. When I put the -f flag, rm doesn’t give an error but the files are still there! wtf