Can't realize alias/substitution function for my .bashrc [duplicate] - linux

This question already has answers here:
Bash script to receive and repass quoted parameters
(2 answers)
Closed 3 years ago.
I'm trying to colourize my find command so I've added this alias function to my .bashrc.
# liberate your find
function find
{
command find $# -exec ls --color=auto -d {} \;
}
But there is unexpected behavior using this code. It drops my quotes.
GNU bash, version 4.4.23(1)-release (x86_64-pc-linux-gnu)
Use my function:
find ./ -name '*.pl' -or -name '*.pm'
Result:
./lib/cover.pm
./lib/db.pm
Using the same find function but built-in:
command find ./ -name '*.pl' -or -name '*.pm'
Result:
./auth.pl
./index.pl
./title.pl
./lib/cover.pm
./lib/db.pm
./fs2db.pl
So the second variant didn't eat my quotes and works as it should.

For reproducing the problem I created all the files as shown in the longer Result in the question.
When I define the function(*) as
function find
{
command find $# -exec ls --color=auto -d {} \;
}
and execute
find ./ -name '*.pl' -or -name '*.pm'
I get an error message
find: paths must precede expression: fs2db.pl
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
because *.pl gets expanded to auth.pl fs2db.pl index.pl title.pl by the shell.
I had to change the function to
function find
{
command find "$#" -exec ls --color=auto -d {} \;
}
to reproduce your problem. (Maybe this depends on the shell. I tested with bash 4.4.19(3)-release)
After set -x you can see what the shell does when executing your function:
$ find ./ -name '*.pl' -or -name '*.pm'
+ find ./ -name '*.pl' -or -name '*.pm'
+ command find ./ -name '*.pl' -or -name '*.pm' -exec ls --color=auto -d '{}' ';'
+ find ./ -name '*.pl' -or -name '*.pm' -exec ls --color=auto -d '{}' ';'
./lib/cover.pm
./lib/db.pm
The difference between executing your function and executing the find command directly is that your function appends an -exec action with the implicit -a (AND) operator. Without an explicit action, find prints all matching results.
You see a result of the operator precedence -a (AND) higher than -o (=-or, OR)
You can compare the output of these 3 commands
command find ./ -name '*.pl' -or -name '*.pm'
command find ./ -name '*.pl' -or -name '*.pm' -print
command find ./ \( -name '*.pl' -or -name '*.pm' \) -print
see http://man7.org/linux/man-pages/man1/find.1.html#NON-BUGS
You can call your function as
find ./ \( -name '*.pl' -or -name '*.pm' \)
to avoid the problem.
(*) This function definition is copied from the question.
You should use the portable POSIX style find() { ... } instead, unless there is a specific requirement for the Korn shell style function find { ... }.

As written, the -exec primary only applies to the code on the right of the -or operator. You need to parenthesize your arguments so that -exec applies to everything that matches. You also need to extract the path from the other arguments (which gets messy if you want to specify multiple paths, as your function would have to decide where to put the parentheses; distinguishing between paths and other expressions would amount to reimplementing a good chunk of find's parsing. I'll assume you are only passing a single path here).
find ()
{
path=$1
shift
command find "$path" \( "$#" \) -exec ls --color=auto -d {} \;
}
Alternatively, you can put the parentheses in the command line, with no change to your current definition.
find ./ \( -name '*.pl' -or -name '*.pm' \)
Your original function runs
find ./ -name '*.pl' -or -name '*.pm' -exec ls --color=auto -d {} \;
which is equivalent to
find ./ -name '*.pl' -or \( -name '*.pm' -exec ls --color=auto -d {} \; \)
with no implicit -print.

Related

How to write a bash script to find files with complex conditions [duplicate]

This question already has answers here:
Expanding a bash array only gives the first element
(1 answer)
Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]
(3 answers)
Closed 11 months ago.
Expected Function: write a bash script showing all the regular files
with filename ended with ".xml" or ".yml" and with path not begined
with "./target/", i.e., exclude the "target" subdirectory.
Example File List: ./a/a1.xml; ./a/a2.txt; ./b/b1.yml; ./target/t.xml;
Example Outout: ./a/a1.xml; ./b/b1.yml
I construct find options in a bash shell script like
#!/bin/bash
find_opts=( -type f -a ( -not -path "./target/*" ) -a ( -false -o -name "*.xml" -o -name "*.yml" ) )
find . $find_opts
But it does not output the expected result. Howver, when I tpye the full command string in bash terminal as follows:
[root#localhost]#find . \( -type f -a \( -not -path "./target/*" \) -a \( -false -o -name "*.xml" -o -name "*.yml" \) \)
it works. What is the problem about the above bash script ?
==============================================================
Someone gives this reference link : Expanding a bash array only gives the first element
It is about "bash array". But it seems that my problem is not about "bash array". Would anyone give any reasons?
Please see How to exclude a directory in find . command to known why I use parentheses whiches look like an array. Anyway, I try two others attempts:
#the first one
find_opts=( -type f -a ( -not -path "./target/*" ) -a ( -false -o -name "*.xml" -o -name "*.yml" ) )
#the second one
find_opts=\\( -type f -a \\( -not -path "./target/*" \\) -a \\( -false -o -name "*.xml" -o -name "*.yml" \\) \\)
#the third one
find_opts=\( -type f -a \( -not -path "./target/*" \) -a \( -false -o -name "*.xml" -o -name "*.yml" \) \)
The first one and the third one give the same output which is unexpeced result. The second one occures an error.
syntax error near unexpected token `('
The problem is still here.
==============================================================
Someone give one more reference:Why does shell ignore quoting characters in arguments passed to it through variables?
It is about how to use bash array to pass arguments and the problem is solved by the following code:
#!/bin/bash
find_opts=(\( -type f -a \( -not -path './target/*' \) -a \( -false -o -name '*.xml' -o -name '*.yml' \) \))
find . "${find_opts[#]}"

Find different types of files and move to a specific directory

Finding *.mkv and *.mp4 works
find /home6/movies/ -name '*.mp4' -o -name '*.mkv'
but moving them for some reason partially fails and moves only mkv files
find /home6/movies/ -name '*.mp4' -o -name '*.mkv' -exec mv {} /home6/archive/ \;
Am I using an incorrect find switch "-o" for this task?
Looks like you need to surround the or expression in parentheses so the exec applies to both matches.
This is a similar question: `find -name` pattern that matches multiple patterns
find /home6/movies/ \( -name '*.mp4' -o -name '*.mkv' \) -exec mv {} /home6/archive/ \;

'Sed' not working on result of 'find' with multiple parameters

I'm trying to do a find and replace function, finding files which match a criteria then find/replace text within them.
Find statement (works find and returns list of files):
find / -type f -name "*.properties" -o -name "*.xml" -not \( -path '/tmp/*' -o -path '/var/tmp/*' \)
Sed find/replace:
sed -i 's/find/replace/g' {} \;
Putting together:
find / -type f -name "*.properties" -o -name "*.xml" -not \( -path '/tmp/*' -o -path '/var/tmp/*' \) -exec sed -i 's/10\.32\.19\.156/10.32.19.165/g' {} \;
However this does not seem to work. Removing some 'find' parameters causes it to work, for example this works:
find / -type f -name "*.properties" -exec sed -i 's/10\.32\.19\.156/10.32.19.165/g' {} \;
How can I get sed to work with the extended 'find' parameters?
Currently these two 'find' statements return exactly the same result in a test folder with only 2 files:
find /var/tmp/ipreplace/ -type f -name "*.properties"
find /var/tmp/ipreplace/ -type f -name "*.properties" -o -name "*.xml" -not \( -path '/tmp/*' -o -path '/var/tmp/*' \)
I guess the use of -path parameter in your find command is wrong.
Try the following:
find / -not \( -path '/tmp' -prune \) -not \( -path '/var/tmp' -prune \) -type f -name "*.properties" -o -name "*.xml" -exec sed -i 's/10\.32\.19\.156/10.32.19.165/g' {} \;
Look at this post for reference

linux find: move files by xargs

How should this be fixed? I am following a tutorial but I receive this error:
$ find ~/Desktop -name “*.jpg” -o -name “*.gif” -o -name “*.png” -print0 | xargs -0 mv –target-directory ~/Pictures
mv: cannot stat `–target-directory': No such file or directory
*I am interested on how to perform this command using xargs!
Using find and exec
$ find ~/Desktop -name "*.jpg" -exec mv '{}' /tmp/target/ \; -or -name "*.gif" -exec mv '{}' /tmp/target/ \; -or -name "*.png" -exec mv '{}' /tmp/target/ \;
Using xargs
$ find ~/Desktop -name "*.jpg" -or -name "*.gif" -or -name "*.png" | xargs -I SRCFILE mv SRCFILE /tmp/target/
You don't need to use xargs, find can execute commands on the matches:
find ~/Desktop -name “*.jpg” -o -name “*.gif” -o -name “*.png” -exec mv \{\} ~/Pictures \;
You can give a command after -exec and before the escaped semicolon \;. The \{\} is replaced with the matching file name.
From man find:
-exec command ;
Execute command; true if 0 status is returned. All following arguments to find are taken to be arguments to the command until an argument consisting of ';' is encountered. The string '{}' is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find. Both of these constructions might need to be escaped (with a '\') or quoted to protect them from expansion by the shell. See the EXAMPLES section for examples of the use of the -exec option. The specified command is run once for each matched file. The command is executed in the starting directory. There are unavoidable security problems surrounding use of the -exec action; you should use the -execdir option instead.
Notice that the semicolon and {} must be escaped.
I believe -target-directory should be --target-directory, or just -t.

linux find command is not taking proper argument

find . -maxdepth 1 ! -path . -type f ! -name "*.gz" ${FILE_PATTERN} -mtime +${DAYS_AFTER_ARCHIVE}
I am trying to execute above command inside the script where ${FILE_PATTERN} and ${DAYS_AFTER_ARCHIVE} are the variable provided while executing the script. The variable value for ${FILE_PATTERN} would be ! -name "*warnings*".
I am looking for executing above command as like below command in script
find . -maxdepth 1 ! -path . -type d ! -name "*warnings*" -mtime +7
I am providing file pattern argument as "! -name "warnings""
but receiving following error message
find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]
suggest on above.
First of all
-name "*.gz" ${FILE_PATTERN}
has too many option values (this is what usually causes the message shown)
If you use bash or similar, escape the exclamation marks
find . -maxdepth 1 \! -path . -type f \! -name "*.gz" ${FILE_PATTERN} -mtime +${DAYS_AFTER_ARCHIVE}
I am providing file pattern argument as "! -name "warnings"" but receiving following error message
You can't combine flags and their values like that. Also, you can't nest " like that. So, it could be like
! -name "$FILE_PATTERN"
If you're using BASH you can make use of BASH arrays:
# find options
FILE_PATTERN=(! -name "warnings*")
# build the find command
cmd=(find . -maxdepth 1 ! -path . -type f \( ! -name "*.gz" "${FILE_PATTERN[#}}" \) -mtime +${DAYS_AFTER_ARCHIVE})
# execute the command
"${cmd[#]}"
If not using BASH then you will have to use eval with caution:
FILE_PATTERN='! -name "warnings*"'
eval find . -maxdepth 1 ! -path . -type f ! -name "*.gz" "${FILE_PATTERN}" -mtime +${DAYS_AFTER_ARCHIVE}

Resources