Linux why can't I pipe find result to rm?
Asked Answered
A

5

94

sorry if this is a noobie question but I can't find a good answer.

To find then remove something I can use

find . -name ".txt" -exec rm "{}" \;

But why can't I just pipe the results to rm like

find . -name ".txt" | rm 

like I would pipe it to grep

find . -name ".txt" | grep a

I've read from somewhere that rm doesn't take input from stdin and therefore I can't pipe it but what does that mean? When I type in rm a.txt it reads from standard input just like I can grep right? Or is there a difference between stdin and command line. Help!

Agma answered 1/12, 2013 at 0:24 Comment(0)
M
166

To expand on @Alex Gitelman's answer: yes, there's a difference between "standard input" and the command line.

When you type rm a.txt b.txt c.txt, the files you list after rm are known as arguments and are made available to rm through a special variable (called argv internally). The standard input, on the other hand, looks to a Unix program like a file named stdin. A program can read data from this "file" just as it would if it opened a regular file on disk and read from that.

rm, like many other programs, takes its arguments from the command line but ignores standard input. You can pipe anything to it you like; it'll just throw that data away. That's where xargs comes in handy. It reads lines on standard input and turns them into command-line arguments, so you can effectively pipe data to the command line of another program. It's a neat trick.

For example:

find . -name ".txt" | xargs rm
find . -name ".txt" | grep "foo" | xargs rm  

Note that this will work incorrectly if there are any filenames containing newlines or spaces. To deal with filenames containing newlines or spaces you should use instead:

find . -name ".txt" -print0 | xargs -0 rm

This will tell find to terminate the results with a null character instead of a newline. However, grep won't work as before then. Instead use this:

find . -name ".txt" | grep "foo" | tr "\n" "\0" | xargs -0 rm

This time tr is used to convert all newlines into null characters.

Marvel answered 1/12, 2013 at 0:39 Comment(8)
thanks, that's a great answer. but how do i tell whether a command takes arguments or stdin since syntactically they are both written as command something. is it included in the man page?Agma
That's not exactly how it works. By convention, many commands which read from files will also read from stdin, but only if no files are named in the command line arguments. Examples: cat, less, grep, sort, sed. But yes, in general, the man page should say whether the command reads from standard input if no arguments are given. (Documenting that is left to the authors of the Unix manual pages. The classic Unix tools tend to be documented more consistently than newer ones. Caveat emptor.)Marvel
@Agma yes it's included in the man page. If the man page doesn't say the command accepts input from stdin then it doesn'tCoopery
Also some commands will allow stdin only when the file name - is given. For example vim: ls | vim - to have the output of ls in vim.Artiodactyl
This worked great for me. Instead of using -name ".txt", I used -print and the rest of it still worked. Thanks!Flaxman
Nowadays you can use the -delete option for the find command: find . -name "*whatever* -delete. I don't know how to verify if this was the case back in 2013Anthe
@Anthe yup, that was true with GNU find (and probably most other finds) in 2013, and it would be a fine way to solve the problem. But the user's question was fundamentally about confusing standard input with command-line arguments, so this answer focused on clearing up that confusion rather than trying to find a totally different solution to the ultimate problem :-)Marvel
@TimPierce Aha, ok. I must have missed that train :) Man pages are great and all but for some reason I prefer googling over TLDR man pages. Maybe it's still worth mentioning for lazy people like me :DAnthe
L
7

Pipe sends output of first command to the standard input of second. rm does not accept standard input so you can't pipe to it. You could use xargs to achieve the same effect. You can find example for xargs specifically for your case in man page for xargs.

Lipscomb answered 1/12, 2013 at 0:26 Comment(0)
K
7

"why can't I pipe find result to rm?"

When you pipe something to a program, the pipe replaces the keyboard input. Keep this in mind and ask youself the next question: What would rm do with a keyboard? Delete your keystrokes ? (a bit silly indeed) Accepting interactive control ? (rm is not interactive except sometimes when it needs confirmation, which indeed can be given by a pipe.)

As a matter of fact, when rm is already running, you can't type commands to let it delete files....so you can't do that with a pipe either.

If you keep in mind that a pipe replaces the keyboard/screen combination, things will immediately appear more logical.

Now the other way around. You can pipe a datastream into grep. Does that mean that you can let grep read the keystrokes from your keyboard as input data instead ?

YES! That is actually what it natively (without piping) does.

(b.t.w. note that you can not pipe nor type the search argument into grep )

So now you know why you can't pipe to rm and expect it to work as a commandline argument.

tl;dr :

Anatomy of a program according to UNIX philosophy:
file in, file out, keyboard in, screen out. -> Pipe does only replace keyboard and screen.

Kelt answered 1/12, 2013 at 2:0 Comment(2)
I am pretty sure rm <(...) will try to delete something like /proc/fd/123.Meuse
Yep, I see my error. I've had not much sleep this week. I've updated it. Thanks for your comment. I appreciate it.Kelt
R
6

An alternative without using pipes :

xargs rm -f <<< $(find . -name ".txt")
Rech answered 9/2, 2016 at 13:19 Comment(0)
M
3

find . -name ".txt" | grep "foo" | xargs -I{} rm {}

Mohn answered 8/5, 2020 at 9:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.