I'm working in win 10 with git-bash. I have a large group of files all of which have no extension. However I've realized that those of type "File" are html files. To select these I have been shown:
$ find -not -name '*.*'
Now I need to rename all these files to add a .html extension (they currently have no extension). I've tried :
$ find -not -name '*.*' -execdir mv {} {}.html
find: missing argument to `-execdir'
How can I rename these files?
You're missing a ; -- a literal semicolon passed to signal the end of the arguments parsed as part of the -exec action. Accepting such a terminator lets find accept other actions following -exec, whereas otherwise any action in that family would need to be the very last argument on the command line.
find -not -name '*.*' -execdir mv -- '{}' '{}.html' ';'
That said, note that the above isn't guaranteed to work at all (or to work with names that start with dashes). More portable would be:
find . -not -name '*.*' -exec sh -c 'for arg do mv -- "$arg" "$arg.html"; done' _ {} +
Note the changes:
The . specifying the directory to start the search at is mandatory in POSIX-standard find; the ability to leave it out on GNU platforms is a nonportable extension.
Running an explicit shell means you don't need {}.html to be supported, and so can work with any compliant find.
The -- ensures that the following arguments are parsed as literal filenames, not options to mv, even if they start with dashes.
In the above, the explicit _ becomes $0 of the shell, so later arguments become $1 and onward -- ie. the array otherwise known as "$#", which for iterates over by default.
Related
I would like to know if there is an alternative to:
find -name "broken" -exec rm '{}' ';'
I want to avoid the semicolon in the end.
I'm looking for a solution to exclude the semicolon. When i just remove it it does not work.
First I want to list certain files and let them be displayed before deleting them.
This should work for all directories and subdirectories. And the "|" (i think its called pipe) and it does not work either.
The ; is part of one of the two supported find -exec syntaxes so you cannot remove it:
-exec utility_name [argument ...] ;
-exec utility_name [argument ...] {} +
The end of the primary expression shall be punctuated by a <semicolon> or by a <plus-sign>. [...]
In certain cases (like yours) you might want to replace the semi-colon with a + (see bellow).
First I want to list certain files and let them be displayed before deleting them
With GNU find:
find -name "broken" -print -delete
With standard find:
find . -name "broken" -print -exec rm -- {} +
I have arraylist of files and I am trying to use rm with xargs to remove files like:
dups=["test.csv","man.csv","teams.csv"]
How can I pass the complete dups array to find and delete these files?
I want to make changes below to make it work
find ${dups[#]} -type f -print0 | xargs -0 rm
Your find command is wrong.
# XXX buggy: read below
find foo bar baz -type f -print0
means look in the paths foo, bar, and baz, and print any actual files within those. (If one of the paths is a directory, it will find all files within that directory. If one of the paths is a file in the current directory, it will certainly find it, but then what do you need find for?)
If these are files in the current directory, simply
rm -- "${dups[#]}"
(notice also how to properly quote the array expansion).
If you want to look in all subdirectories for files with these names, you will need something like
find . -type f \( -name "test.csv" -o -name "man.csv" -o -name "teams.csv" \) -delete
or perhaps
find . -type f -regextype egrep -regex '.*/(test\.csv|man\.csv|teams\.csv)' -delete
though the -regex features are somewhat platform-dependent (try find -E instead of find -regextype egrep on *BSD/MacOS to enable ERE regex support).
Notice also how find has a built-in predicate -delete so you don't need the external utility rm at all. (Though if you wanted to run a different utility, find -exec utility {} + is still more efficient than xargs. Some really old find implementations didn't have the + syntax for -exec but you seem to be on Linux where it is widely supported.)
Building this command line from an array is not entirely trivial; I have proposed a duplicate which has a solution to a similar problem. But of course, if you are building the command from Java, it should be easy to figure out how to do this on the Java side instead of passing in an array to Bash; and then, you don't need Bash at all (you can pass this to find directly, or at least use sh instead of bash because the command doesn't require any Bash features).
I'm not a Java person, but from Python this would look like
import subprocess
command = ["find", ".", "-type", "f"]
prefix = "("
for filename in dups:
command.extend([prefix, "-name", filename])
prefix = "-o"
command.extend([")", "-delete"])
subprocess.run(command, check=True, encoding="utf-8")
Notice how the backslashes and quotes are not necessary when there is no shell involved.
i'm trying to write out a list of the names of everything under the /etc directory that are executable to all other users and whose name starts or ends with a number.
find /etc "(" -name "[0-9]*" -o -name "*[0-9]" ")" -perm -o=x -print
But every time I get a wrong answer, can you help?
If you're using the zsh shell, you can get that list of files with its advanced filename generation globbing; no external programs needed. In particular, using a recursive glob, alternation, and a glob qualifier that matches world-executable files:
zsh$ printf "%s\n" /etc/**/([0-9]*|*[0-9])(X)
/etc/alternatives/animate-im6
/etc/alternatives/c89
/etc/alternatives/c99
/etc/alternatives/compare-im6
/etc/alternatives/composite-im6
...
/etc/X11
/etc/X11/fonts/Type1
/etc/xdg/xdg-xubuntu/xfce4
/etc/xdg/xfce4
/etc/xfce4
Do a setopt glob_dots first to match filenames starting with . like find does. Otherwise they get skipped.
If you're using find, you need the -mode argument to -perm to select files with at least the given permission bits (Which is actually what you have in your question and works for me)
find /etc \( -name "[0-9]*" -o -name "*[0-9]" \) -perm -o=x
here are some names:
El Peulo'Pasa, Van O'Driscoll, Mike_Willam
how to filter the name contains ', using POSIX in bash by command find?
if I use the following command,
find . -maxdepth 1 -mindepth 1 -type d -regex '^.*[']*$' -print
Bash runs into a problem because the syntax ' will automatically convert the input to string
You don't need -regex (which is a non-POSIX action) for this at all; -name is more than adequate. (-mindepth and -maxdepth are also extensions that aren't present in the POSIX standard).
To make a ' literal, put it inside double quotes, or in an unquoted context and precede it with a backslash:
find . -maxdepth 1 -mindepth 1 -type d -name "*'*" -print
...or the 100% identical but harder-to-read command line...
find . -maxdepth 1 -mindepth 1 -type d -name '*'\''*' -print
If you're just searching the current directory (and not its subdirectories), you don't even need find, just a wildcard ("glob") expression:
ls *\'*
(Note that the ' must be escaped or double-quoted, but the asterisks must not be.)
If you want to do operations on these files, you can either use that wildcard expression directly:
dosomethingwith *\'*
# or
for file in *\'*; do
dosomethingwith "$file"
done
...or if you're using bash, store the filenames in an array, then use that. This involves getting the quoting just right, to avoid trouble with other weird characters in filenames (e.g. spaces):
filelist=( *\'* )
dosomethingwith "${filelist[#]}"
# or
for file in "${filelist[#]}"; do
dosomethingwith "$file"
done
The note here is that arrays are not part of the POSIX shell standard; they work in some shells (bash, ksh, zsh, etc), but not in others (e.g. dash). If you want to use arrays, be sure to use the right shebang to get the shell you want (and don't override it by running the script with sh scriptname).
I am trying to find all directories that start with a year in brackets, such as this:
[1990] Nature Documentary
and then rename them removing brackets and inserting a dash in between.
1990 - Nature Documentary
The find command below seems to find the results, however I could not prefix the pattern with ^ to mark start of directory name otherwise its not returning hits.
I am pretty sure I need to use -exec or -execdir, but I am not sure how to store the found pattern and manipulate it.
find . -type d -name '\[[[:digit:]][[:digit:]][[:digit:]][[:digit:]]] *'
With [p]rename:
-depth -exec prename -n 's/\[(\d{4})]([^\/]+)$/$1 -$2/' {} +
Drop -n if the output looks good.
Without it, you'd need a shell script with several hardly intelligible parameter expansions there:
-depth -exec sh -c '
for dp; do
yr=${dp##*/[} yr=${yr%%]*}
echo mv "$dp" "${dp%/*}/$yr -${dp##*/\[????]}"
done' sh {} +
Remove echo to apply changes.
You can use the rename command
find . -type d -name '\[[[:digit:]][[:digit:]][[:digit:]][[:digit:]]\] *'| rename -n 's/(\[\d{4}\]) ([\w,\s]+)+$/$1 - $2/'
Note: The effect will not take place until you delete the -n option.