58

I wanted to delete some package in my home file, but the filename was too long (google-chrome-stable_current_i386.deb). So, I decided to use the command ls|grep chrome|rm to pipe the files to grep to filter out the chrome file, and then remove it. It didn't work, so I would like to see how I can do this.

SpecialBomb
  • 1,968
  • 24
    rm -i *chrome*.deb – Lambert Dec 07 '15 at 14:46
  • 30
    Generally speaking I dislike when people post "don't do that" answers, but this is one of the best examples of an XY problem I've ever seen. Your real question is "how do I quickly delete a file with a long filename, without typing the whole thing out", and there are lots of good ways that don't involve grepping the output from ls – Michael Mrozek Dec 07 '15 at 19:00
  • 6
    Just use find - find . -name "*chrome*" -delete. – Boris the Spider Dec 08 '15 at 09:55
  • i like saying popular stuff! – mikeserv Dec 08 '15 at 15:56
  • 1
    Hello there, do you have a moment to talk about our lord and saviour? Tab-autocomplete? Hmm... no you say? Have you heard of xargs? – Alec Teal Dec 09 '15 at 02:56
  • 2
    Obligatory links: http://mywiki.wooledge.org/ParsingLs and https://unix.stackexchange.com/questions/128985/why-not-parse-ls – scai Dec 09 '15 at 10:17
  • 1
    The simple way is to type "google-chrome" and hit the tab key. – micheal65536 Dec 09 '15 at 11:02
  • 3
    My two cents as a StackExchange user: in the future, ask your question in a more generic way while providing details about what you have tried. "How do I delete any files in a folder with a certain string in the filename?" might have been a better way to phrase it. Then you could simply add, as an aside, that you tried piping ls through grep to rm but couldn't get it to work. That way you'll get one or two good answers about the "correct" or "easiest" way to accomplish your task rather than forcing someone to give you a good answer about an inefficient solution. – Adrian Dec 09 '15 at 21:16
  • @MichealJohnson This really isn't the best way to do it, especially if you are not using a terminal emulator. – SpecialBomb Mar 16 '16 at 20:21
  • 1
    @SpecialBomb He said "the filename was too long". I assume from the length of the filename that he doesn't mean "the filename was too long for the system to process" but rather "the filename was too long for me to type" so he's using the command line interactively and is trying to shortcut the typing of long filenames. To answer the question, no, the tab key isn't always a replacement for "pipe ls to grep and delete the files filtered by grep", but this is a classic example of asking the wrong question. – micheal65536 Mar 17 '16 at 13:39

6 Answers6

68

This almost made me wince.

  1. You might want to stop pointing that shotgun at your foot. Basically any kind of parsing of ls is going to be more complicated and error-prone than established methods like find [...] -exec or globs.
  2. Unless someone installed a troll distro for you, your shell has Tab completion. Just type rm google and press Tab. If it doesn't complete immediately, press Tab again to see a list of matching files. Type more characters of the filename to narrow it down until it does complete, then run the command.
  3. Pipes != parameters. Standard input is a binary data stream which can be fed to a command asynchronously. Parameters are space separated strings which are passed once and only once to a command when running it. These are very rarely interchangeable.
l0b0
  • 51,350
  • 4
    Sorry if it seemed like a n00b question. I knew it was okay to use generally, as the package stated was the only package with "chrome" stated in it. Therefore, using grep would filter out only one file. I am pretty new to linux, and I have already done plenty of bash scripts, but I don't know everything yet :P. Harsh, dude. – SpecialBomb Dec 07 '15 at 20:45
  • 7
    Didn't mean to be harsh, but the shell is a harsh and unforgiving environment, hidden behind a kinda user friendly surface. Doing things safely is difficult enough, never mind securely or even correctly. – l0b0 Dec 07 '15 at 22:12
  • 4
    @SpecialBomb This is the only correct answer. Explaining how to correctly aim said shotgun to cleanly cut off one's foot, as the other answers do, is certainly not the way to go. Be especially careful of blindly executing rm - it's probably the single more common way to fry your distro. – Boris the Spider Dec 08 '15 at 09:58
  • @BoristheSpider Heh, I've definitely already fried Ubuntu twice installing AMD graphics card drivers. I already asked a question about that though. AMD has been horrible, so I asked if there were any common fixes or better methods of gaming with Linux on an AMD card. – SpecialBomb Dec 09 '15 at 00:50
  • What would be best for a Makefile task that removes all files starting with 'foo' and ending with 'bar'? I had in mind ls | grep "foo*bar" | rm – stevec May 09 '20 at 14:01
  • I think I found the answer from the link: find . -maxdepth 1 -name '*chrome*' -delete To the uninitiated, this doesn't look that different, with the exception that it has the maxdepth parameter (which is obviously a great safety feature). Is there anything else to be aware of? – stevec May 09 '20 at 14:07
  • 1
    @stevec If you want clarity around how find works I would recommend man find or asking another question here. "anything else to be aware of" is a theme big enough for a book. – l0b0 May 09 '20 at 20:09
  • 1
    It bugs me that this is the accepted answer, but it isn't the answer. It's advice for a path to the answer... and all that was needed as icing on the cake was the actual way to use find properly. Voting up the other answer that provided that instead. – Brandon Aaskov Jul 20 '21 at 01:57
59

You had the right idea, just missed some details. Since you're dealing with a list coming to STDIN and rm expects parameters, you need to use xargs.

Thus:

ls | grep chrome | xargs rm

Should give you what you want.

Note that if you want to delete everything other than the chrome file, you can simply add -v to the grep statement.

Note that, per the other answers to this question, this is probably a bad way of accomplishing what you want to accomplish.

John
  • 17,011
  • With -v, wouldn't this delete everything except for files that match chrome? I don't think that's what the OP wants... – akivajgordon Dec 07 '15 at 18:24
  • Looking again, you're correct. The OP wants to "filter out the chrome file", but then wants to remove it. – John Dec 07 '15 at 18:44
  • This seems needlessly complex, compared to a simple "rm -i chrome" – chander Dec 07 '15 at 19:32
  • What specifically does 'xargs' do? – SpecialBomb Dec 07 '15 at 20:39
  • xargs takes STDIN and turns it into command line parameters for the specified command given to it. – John Dec 07 '15 at 20:40
  • That may be an oversimplification of what xargs does, admittedly, but it is an answer I feel is sufficient within the context of this question. – John Dec 07 '15 at 20:43
  • 5
    The accepted answer works, but see the answer below which explains why it's a bad idea. (Posting this comment here only for the sake of those who stop as soon as they see an "accepted" checkmark.) – Jim Davis Dec 08 '15 at 17:24
  • 2
    This, the accepted answer "works" only if there are no "weird" filenames in the ls output. Filenames with spaces will wreck this. This is also dangerous if someone can construct a filename with special sauce in the directory where you run this. As more than one person has already pointed out, don't parse the output of ls! – Stephen P Dec 09 '15 at 02:15
40

You can also use the find command with a wildcard:

find . -maxdepth 1 -name '*chrome*' -delete

Note that the "-maxdepth" argument ensures that find only works in the current directory, and doesn't recurse into subdirectories.

chander
  • 590
  • 3
  • 4
  • 1
    Suggestions for improvements: (a) use -iname for case-insensitive filenames (b) pipe to xargs using -print0 flag for find and -0 flag for xargs. The use of xargs allows you to do things like "interactive" (confirm delete foo?) deletes. – Christopher Schultz Dec 07 '15 at 22:15
  • 3
    No need to pipe to xargs if you use -exec option: find . -name '*chrome*' -exec rm -i {} + – Johnny Dec 07 '15 at 23:53
  • 4
    Won't this also delete recursively into subdirectories? You may need to specify maxdepth to be 1. – Tyzoid Dec 08 '15 at 12:48
16

Never parse the output of ls
My suggestion is to avoid to parse the output of ls [1], even more if in conjunction with the del command. This for many reasons mainly related to unexpected and not usual characters allowed in the file name.

Even when you should expect that the filenames belonging to Linux packages will "behave well", this problem can nonetheless appears if other files are present in the same directory but you didn't know or notice.

It's better to use find, the tab expansion (start to write the name and press Tab), the file name expansion [2] as *MyKey*...


A fast solution
Since you want to select all the packages (that finish with .deb) with "google" inside you can build your request with the wildcard * *google*.deb and do a simple

rm -i *google*.deb 

that will select each filename with "google" in the middle that will finish for .deb present in the current directory. The option -i (interactive) will prompt for the confirmation, a good habit when you delete files with the parameter expansion.


A solution close to the philosophy of your attempt
If your purpose is to build your commandline piece after piece, so you have done ls, after ls | grep google, and only after you checked your output you can execute it in a subshell $(...) with

rm -i $(ls | grep google)

A faster and more dangerous way [3], is to use !!

ls | grep google
rm -i $(!!)

that will execute the last command finished in your history. You can protect yourself from the fact that you have no visual control of the line that you are going to execute if you have enabled in advance the shell options histverify with shopt -s histverify.

Hastur
  • 2,355
5
touch 0 1 2 3 4 5 6 7 8 9
find . -name \[0-9] -ok rm {} \;

< rm ... ./0 > ? y
< rm ... ./9 > ? y
< rm ... ./8 > ? y
< rm ... ./7 > ? y
< rm ... ./6 > ? y
< rm ... ./5 > ? y
< rm ... ./4 > ? y
< rm ... ./3 > ? y
< rm ... ./2 > ? y
< rm ... ./1 > ? y
^C

...use -name '*c*.deb' or some other pattern as suits you instead.

mikeserv
  • 58,310
3

rm doesn't accept input from stdin. You'll need to do something like ls google-chrome* | xargs rm

David King
  • 3,147
  • 9
  • 23