unix: files starting with - wildcard - shell

I want to rm or cp a bunch of files with common extensions, some of them start with a - and so unix complains about unknown options. What can I do?
rm *csv

man rm:
To remove a file whose name starts with a '-', for example '-foo',
use one of these commands:
rm -- -foo
rm ./-foo
So:
$ touch -- -test test
$ rm -- *test
rm: remove regular empty file 'test'? y
rm: remove regular empty file '-test'? y
$

Also, quoting works to inhibit globbing if there's, say, a literal asterisk in the name:
rm "*cvs"
Sometimes it might be useful to use the interactive option and confirm the file you want to delete:
rm -i -- *
This is handy if filenames have characters hard to type on your keyboard.

Related

How to delete all files and folders in given folder except one and folder itself?

I currently use following, but dist folder is also deleted.
find ./dist ! -name '.borgignore' -prune -exec rm -r {} +
I am aware of ./dist/*, but also want to delete dot files.
You could simply use GLOBIGNORE, a plain bash feature:
$ GLOBIGNORE=./dist/.borgignore
$ printf '%s\n' ./dist/*
Check the output and if it's OK:
$ rm -rf ./dist/*
Explanations: when GLOBIGNORE is set to a colon-separated list of patterns, pathname expansion ignores the files that also match one of the patterns, and the dotglob option is automatically enabled: that is, dot-files match the ./dist/* pattern, except the ./dist/. and ./dist/.. filenames (they would have to be listed explicitly to match).

Bash script: variable output to rm with single quotes

I'm trying to pass parameter to rm in bash script to clean my system automatically. For example, I want to remove everything except the *.doc files. So I wrote the following codes.
#!/bin/bash
remove_Target="!*.txt"
rm $remove_Target
However, the output always say
rm: cannot remove ‘!*.txt’: No such file or directory
It is obviously that bash script add single quotes for me when passing the variable to rm. How can I remove the single quotes?
Using Bash
Suppose that we have a directory with three files
$ ls
a.py b.py c.doc
To delete all except *.doc:
$ shopt -s extglob
$ rm !(*.doc)
$ ls
c.doc
!(*.doc) is an extended shell glob, or extglob, that matches all files except those ending in .doc.
The extglob feature requires a modern bash.
Using find
Alternatively:
find . -maxdepth 1 -type f ! -name '*.doc' -delete

Truncate a directory in bash

Is there some elegant/simple way to delete the folder's contents in such a way there's no error output if it is empty?
The following command
$ rm -r $dir/*
doesn't work if the directory is empty, since in such a case, the wilcard * is not expanded and you get an error saying that rm cannot find file *.
Of course, the standard way is check if it is empty (with ls $dir | wc -w or find $dir -link 2 or any other related command), and deleting its contents otherwise.
Is there an alternative way not to check folder contents and only "truncate" the directory instead?
Bash
Simply,
$ rm -rf dir/*
(By default I believe) Bash doesn't complain about not finding anything with the glob. It just passes your literal glob through to your command:
$ echo dir/*
dir/*
When rm doesn't find a filename that has the literal glob character, it complains about not finding the file it's been asked to delete:
$ rm "dir/*"
rm: cannot remove ‘dir/*’: No such file or directory
$ echo $?
1
But if you force it, it won't complain:
$ rm -f "dir/*"
$ echo $?
0
I don't know if that refrain-from-complain is POSIX.
Do note, however, that if you don't have the shell option "dotglob" set, you'll miss files that start with a dot, AKA "hidden" files.
Generally
Zsh doesn't pass the literal glob through by default. You have to ask for it with "set -o nonomatch".
$ echo dir/*
zsh: no matches found: dir/*
$ echo $?
1
$ set -o nonomatch
$ echo dir/*
dir/*
For compatibility, I wouldn't use the above modern-Bash-specific "rm -rf dir/*", but would use the more general, widely-compatible solution:
$ find dir -mindepth 1 -delete
Find all files in "dir" at a minimum depth of 1 ("dir" itself is at depth 0), and delete them.
You can use rm -rf:
rm -rf "$dir"/*
As per man bash:
-f, --force
ignore nonexistent files and arguments, never prompt
rm -rf dir/*
does not delete hidden files which name starts with dot.
This is quite weird, when bash glob the *, it does not include .* files.
mkdir -p dir
touch dir/.a
rm -fr dir/*
ls dir/.a && echo I am not deleted
output is
dir/.a
I am not deleted
Besides, the rm -fr dir/* has another disadvantage: when there are too many files in the dir, the rm command will get too many arguments and results in error too many arguments. Also, it is very slow in that case.
Seems that the most reliable and fastest way is
find dir -mindepth 1 -delete

Explaining the rm ./-rf "trick"

This question states:
It is amazing how many users don't know about the rm ./-rf or rm -- -rf tricks.
I am afraid to try these, but curious as to what they do. They are also very difficult to search...
Can someone enlighten me?
rm ./-rf and/or rm -- -rf would attempt to remove a file named, specifically, -rf
The only trick here is that you normally can't delete a file that starts with a "-" because the command will assume it's a command argument. By preceding the file with a full path, or using the -- option (which means, end all options) the command will no longer assume it's an argument.
It should be noted that the -- version of this trick may or may not work with all shell commands either, so it's best to use the first version.
If you have a file named -rf in your directory, it is difficult to remove that file if you don't know the trick. That's because:
rm -rf
supplies two command line options (-r and -f) as a single argument, and tells rm to recursively and forcibly remove things.
If you write:
rm ./-rf
the argument does not start with a dash any more, so it is simply a file name. Similarly, by common (but not universal) convention, -- marks the end of the option arguments and anything afterwards is no longer an option (which usually means it is a file name). So:
rm -- -rf
removes the file because rm knows that the arguments that follow the -- are file names, not options for it to process.
The file -rf is even more dangerous if you use wildcards:
rm *rf*
Suddenly, this will remove directories as well as files (but won't harm the file -rf).
Not a complete answer, as I think the other answers give good explanations.
When I'm unsure what a given rm invocation is going to delete, I try to remember to simply ls the file list first to make sure it is actually what I want to delete:
$ ls -rf
-rf .. .
$
OK, clearly thats not right, lets try again:
$ ls ./-rf
./-rf
$
Thats better. Lets do a history replacement of ls with rm -v (-v just for extra paranoia/checking) and do the actual delete:
$ rm -v !!:*
rm -v ./-rf
removed `./-rf'
$
This also works nicely with wildcards, brace expansions, etc, when you're not sure what the expansion will be exactly.
Also if you're wondering how files like -rf get created in the first place, its astonishingly easy if you mess up a redirection a bit:
$ ls
$ echo > -rf
$ ls
-rf
$

How to remove files starting with double hyphen?

I have some files on my Unix machine that start with
--
e.g. --testings.html
If I try to remove it I get the following error:
cb0$ rm --testings.html
rm: illegal option -- -
usage: rm [-f | -i] [-dPRrvW] file ...
unlink file
I tried
rm "--testings.html" || rm '--testings.html'
but nothing works.
How can I remove such files on terminal?
rm -- --testings.html
The -- option tells rm to treat all further arguments as file names, not as options, even if they start with -.
This isn't particular to the rm command. The getopt function implements it, and many (all?) UNIX-style commands treat it the same way: -- terminates option processing, and anything after it is a regular argument.
http://www.gnu.org/software/hello/manual/libc/Using-Getopt.html#Using-Getopt
rm -- --somefile
While that works, it's a solution that relies on rm using getopts for parsing its options. There are applications out there that do their own parsing and will puke on that too (because they might not necessarily implement the "-- means end of options" logic).
Because of that, the solution you should drive through your skull is this one:
rm ./--somefile
It will always work, because this way your arguments never begin with a -.
Moreover, if you're trying to make really decent shell scripts; you should technically be putting ./ in front of all your filename parameter expansions to prevent your scripts from breaking due to funky filename input (or to prevent them being abused/exploited to do things they're not supposed to do: for instance, rm will delete files but skip over directories; while rm -rf * will delete everything. Passing a filename of "-rf" to a script or somebody touch ~victim/-rf'ing could in this way be used to change its behaviour with really bad consequences).
Either rm -- --testings.html or rm ./--testings.html.
rm -- --testings.html
Yet another way to do it is to use find ... -name "--*" -delete
touch -- --file
find -x . -mindepth 1 -maxdepth 1 -name "--*" -delete
For a more generalised approach for deleting files with impossible characters in the filename, one option is to use the inode of the file.
It can be obtained via ls -i.
e.g.
$ ls -lai | grep -i test
452998712 -rw-r--r-- 1 dim dim 6 2009-05-22 21:50 --testings.html
And to erase it, with the help of find:
$ find ./ -inum 452998712 -exec rm \{\} \;
This process can be beneficial when dealing with lots of files with filename peculiarities, as it can be easily scripted.
rm ./--testings.html
or
rm -- --testings.html

Resources