Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this bash function for removing all but some files work?

Tags:

bash

I've found the following script on commandlinefu.com (the example is not online anymore):

rmbut() { 
    local x=("$@")
    IFS=, rm -rf *[!"${x[*]}"] 
}

It deletes all files and directories but the ones named on the command line.

Could you explain the following:

  • What is happening on the first line? $@ means all arguments, but why is it put inside parentheses?
  • I've read about IFS but never actually used it, what is its usage here?
  • What is achieved with *[!"${x[*]}"]? I can't understand how to split it into something I know.
like image 692
Alberto Zaccagni Avatar asked Jan 26 '11 13:01

Alberto Zaccagni


People also ask

How do you remove all files except some?

Using Extended Globbing and Pattern Matching Operators Also, with the ! operator, we can exclude all files we don't want glob to match during deletion. Let's look at the list of pattern matching operators: ?(pattern-list) matches at least zero and at most one occurrence.

How do I delete all files except a specific file extension?

Microsoft Windows Browse to the folder containing the files. Click the Type column heading to sort all files by the type of files. Highlight all the files you want to keep by clicking the first file type, hold down Shift , and click the last file.

How do I delete all files in a directory in bash?

To remove a directory and all its contents, including any subdirectories and files, use the rm command with the recursive option, -r .


2 Answers

local x=("$@") creates an array which is a copy of all the arguments ($@ is itself an array).

IFS=, sets the internal field separator to a comma.

IFS=, rm -rf *[!"${x[*]}"] says to remove all files that do not end in any character passed as arguments. Since * is used as the index to the array and the variable is quoted it is expanded to a single string and the spaces that would normally separate the elements of the array are replaced by the contents of IFS (a comma in this case).

rmbut a b c

resolves to rm -rf *[!a,b,c] which would also not remove files that end in a comma.

I think the function could be simplified to:

rmbut() { 
    IFS= rm -rf *[!"$*"] 
}

but its behavior would be subtly different. This version sets IFS to null so the example above would resolve to rm -rf *[!abc] which would remove files that end in a comma (a comma would have to be passed explicitly as an argument to preserve such files). However, that behavior could be returned by setting IFS=, (it's simply not necessary to copy the array).

like image 110
Dennis Williamson Avatar answered Sep 22 '22 13:09

Dennis Williamson


# create an array x, containing arguments to the function
local x=("$@")

# unset the IFS variable. This make double quoted arrays expand to single words w/o separators
IFS=

# remove files matching a pattern, i.e. not ending with a character from the array x
rm -rf *[!"${x[*]}"]

like image 39
Eugene Yarmash Avatar answered Sep 26 '22 13:09

Eugene Yarmash