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!

  • Arthur BesseA
    link
    fedilink
    arrow-up
    1
    ·
    4 hours ago

    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 with echo in place of rm just to be sure i am getting the results i expect. also when i re-run it actually using rm, i tend to use the -v option (which tells rm to print what it is doing) to reassure myself that i’ve just deleted what i wanted to and nothing else.

    • skaarl@feddit.nlOP
      link
      fedilink
      English
      arrow-up
      3
      ·
      10 hours ago

      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

      • harsh3466
        link
        fedilink
        arrow-up
        1
        ·
        6 hours ago

        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.

      • harsh3466
        link
        fedilink
        arrow-up
        1
        ·
        6 hours ago

        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.

        • skaarl@feddit.nlOP
          link
          fedilink
          English
          arrow-up
          1
          ·
          6 hours ago

          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

          • harsh3466
            link
            fedilink
            arrow-up
            1
            ·
            edit-2
            6 hours ago

            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

            • skaarl@feddit.nlOP
              link
              fedilink
              English
              arrow-up
              1
              ·
              edit-2
              5 hours ago

              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 XD

              • harsh3466
                link
                fedilink
                arrow-up
                2
                ·
                5 hours ago

                Here’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)

                • harsh3466
                  link
                  fedilink
                  arrow-up
                  3
                  ·
                  edit-2
                  5 hours ago

                  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 GNU find, which outputs the full path of what it finds. The arguments are:

                  ./"$f" - This tells find where to start its search. I double qouted the $f variable to properly expand the directory name even if it has nonstandard characters in it like spaces.

                  -type f - This tells find what kind of file object to look for. So it’s two parts. -type to tell find there will be a specific type to look for, and the f flag, which means file. Meaning, it will only find files

                  The output of find is not sorted alaphabetically, so before piping the output to tail, we first pipe it to sort, which by default will sort alphanumerically, which we then pipe to tail to grab just the last two files, and finally we get to the xargs bit.

                  Here I added the -n 1 argument to xargs to get it to work on the files one at a time. This isn’t actually necessary. You could just run it as xargs rm. I didn’t realize that before I posted the command. (I’m still learning too! The learning never ends. :D )

      • huf [he/him]@hexbear.net
        link
        fedilink
        English
        arrow-up
        1
        ·
        10 hours ago

        this will break pretty badly if you have filenames with spaces or newlines in them. so to make this actually robust, you now get to learn about find -print0, xargs -0, and why you always, always need to add "" around variables in bash.

        • skaarl@feddit.nlOP
          link
          fedilink
          English
          arrow-up
          1
          ·
          6 hours ago

          Can find be used with tail?

          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

          • huf [he/him]@hexbear.net
            link
            fedilink
            English
            arrow-up
            1
            ·
            6 hours ago

            find can be used with tail, but if you’re doing nul-delimited stuff (-print0, -0), then you’ll want tail to run in nul-delimited mode too (-z apparently).

            or you can say “fuck files with newlines in them, i aint supporting that shit”, and then you just need the “” to still support filenames with spaces.

            • skaarl@feddit.nlOP
              link
              fedilink
              English
              arrow-up
              1
              ·
              5 hours ago

              Hmm, thanks. It’s gonna take a while to unpack this ':D

              There are no newlines in the names, and all spaces have been replaced with a dash.

      • calm.like.a.bomb@lemmy.dbzer0.com
        link
        fedilink
        English
        arrow-up
        1
        ·
        10 hours ago

        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 interaction

        • skaarl@feddit.nlOP
          link
          fedilink
          English
          arrow-up
          1
          ·
          6 hours ago

          Oh 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