Python subprocess doesn't work with my find expression - subprocess

This works in my terminal:
find "/tmp/pic" \( -iname *.JPG \) | parallel -j 3 convert {} -resize 1920x -quality 60 "/tmp/pic2/{/}"
This doesn't work with python:
from subprocess import call
import shlex
call(shlex.split('find "/tmp/pic" \( -iname *.JPG \) | parallel -j 3 convert {} -resize 1920x -quality 60 "/tmp/pic2/{/}"'), shell=True)
I've found this question with the same error. But the suggested solution (shell=True) doesn't work. The solution with os.walk described of #abarnert sounds logical, but I just would like to understand what is wrong in my code.

The shell does the splitting for you:
from subprocess import call
call('find "/tmp/pic" \( -iname *.JPG \) | parallel -j 3 convert {} -resize 1920x -quality 6\
0 "/tmp/pic2/{/}"', shell=True)

Related

How to exclude symlink files from find command?

I use the following command to do some processing on text files,however it changes the symlink files aswell,is there a way to exclude symlink files from the find command and include only the actual files?
find . \( -name "*.txt" ! -name "release.txt" \) | xargs -t -n1 sed -i '' -e '/^#/d;/^nocrc=/d;/acaddr=/d;/^$/d;s/[ \t]*$//'

Shell alias input search syntax for a negated find search

Mac OSX Bash Shell
I want to use find to identify anything (directories or files) which do not follow an input pattern.
This works fine:
find . -path /Users/Me/Library -prune -o \! \( -path '*.jpg' \)
However I want to have a general ability to do from a bash alias or function eg:
alias negate_find="find . -path /Users/Me/Library -prune -o \! \( -path ' "$1" ' \)"
To allow shell input of the search term (which may contain spaces). The current syntax does not work, returning unknown primary or operator on invocation. Grateful for assistance in what I am doing wrong.
Not entirely sure why, but separating the input parameter into its own string seemed to work. Here it is as a working shell function and case invariant.
negate_find () {
search="$1"
lowersearch=$(echo "$search" | awk '{print tolower($0)}')
uppersearch=$(echo "$search" | awk '{print toupper($0)}')
echo "search = $search"
find . -path $HOME/Library -prune -o \! \( -path "$lowersearch" \) -a \! \( -path "$uppersearch" \)
}
export -f negate_find

Find not matching a grep, after results

Hopefully this is a simple one, but I can't figure it out. I'm trying to work out why this command isn't finding any results:
find . -iname '*.cgi' -o -iname '*.txt' -o -iname '*.htm' -o -iname '*.html' -o -iname '*.php' -exec grep -l 'community.cgi' {} +
If I simplify it and just do:
find . -iname '*.cgi' -o -iname '*.txt' -o -iname '*.htm' -o -iname '*.html' -o -iname '*.php'
Then I get the list of files I'm expecting. For some reason the -exec part doesn't seem to be what I'm expecting. If I just run a basic grep on ALL files, I get the list of files as well:
grep -l 'community.cgi' .
Logical-and binds tighter than logical-or. Try:
find . \( -iname '*.cgi' -o -iname '*.txt' -o -iname '*.htm' -o -iname '*.html' -o -iname '*.php' \) -exec grep -l 'community.cgi' {} +
Here, parens (which have to be escaped to pass through the shell) are used to bind the -iname tests together so that -exec runs if any one of those tests is true.

find using multiple name patterns

I have this working fine for me:
find Sources/$1-$2 -name '*' |xargs perl -pi -e "s/domain.com/$2/g"
But when I change it to the following it doesn't:
find Sources/$1-$2 -name '*.php,*.rb' |xargs perl -pi -e "s/domain.com/$2/g"
What wrong?
Here's some explanation behind the solution that others have provided.
The tests in a find command are combined with Boolean operators:
-a -and
-o -or
! -not
If you don't supply an operator, -and is the default.
find . -type f -name '*.rb' # The command as entered.
find . -type f -a -name '*.rb' # Behind the scenes.
Your search failed because it didn't find any matching files:
# Would find only files with bizarre names like 'foo.php,bar.rb'
find . -name '*.php,*.rb'
You need to supply the file extensions as separate -name tests, combined in an OR fashion.
find . -name '*.php' -o -name '*.rb'
you have to write it as:
find Sources/$1-$2 -name '*.php' -o -name '*.rb' ....
I'm guessing that you want all files then end in .php and .rb.
Try find Sources/$1-$2 \( -iname "*.php" -o -iname "*.rb" \) -print |xargs perl -pi -e "s/domain.com/$2/g"
It is much better filtering out find's result with [ef]grep. Why?
Because you can fed the grep pattern as an argument, or can read it from the config or soo. It is much easier to write: grep "$PATTERN" as constructing long find arguments with '-o'. (ofc, here are situations, where find args are better), but not in your case.
The cost is one more process. So, for you example is easy to write a script myscript.sh
find Sources/$1-$2 -print | egrep -i "$3" | xargs ...
you can call it
./myscript.sh aaa bbb ".(php|rb)$"
and the result will equivalent to more complicated
find Sources/$1-$2 \( -iname '*.php' -o -iname '*.rb' \) | xargs ...
but
why bother? If you have bash4+, (and shopt -s globstar in your .bashrc) you can simple write:
perl -pi -e '.....' Sources/aaa-bbb/**/*.{rb,php}
the ** is like a find -name.
By the way, xargs is not needed here.
find Sources/$1-$2 \( -name '*.php' -o -name '*.rb' \) \
-exec perl -i -pe "s/domain\.com/$2/g" {} +
Also notice the "." in /domain.com/ needs to be escaped.

Query ragarding Solaris find command with -exec option

I want to create tar file with all the output files resulting from executing find command.
I tried the following command:
find . \(-name "*.log" -o -name "*.log.*" \) -mtime +7 -exec tar cvf test.tar.gz {} \;
But it is including only the last found file in the test.tar file. How to include all files in test.tar file?
Regards
Chaitanya
Use command line substitution:
tar cf test.tar $(find . \(-name "*.log" -o -name "*.log.*" \) -mtime +7)
What this does is run the command in $() and makes the output the command line arguments of the outer command.
This uses the more modern bash notation. If you are not using bash, you can also use backticks which should work with most shells:
tar cf test.tar `find . \(-name "*.log" -o -name "*.log.*" \) -mtime +7`
While backticks are more portable, the $() notation is easier if you need to nest command line substitution.
You want to pipe the file names found by find into tar.
find . \(-name "*.log" -o -name "*.log.*" \) -mtime +7 -exec tar cvf test.tar.gz {} \;
But it is including only the last found file in the test.tar file.
That's because for every file it finds it is running a new tar command that overwrites the tar file from the previous command.
You can make find batch the files together by changing the \; to a + but if there's more
files than can be listed at once, find will still run multiple commands, each overwriting the tar file from the previous one. You could pipe the output through xargs but it has the same issue of possibly running the command multiple times. The command line substitution recommended above is the safest way I know of to do it, ensuring that tar can only be called once -- but if too many files are found, it may give an error about the command line being too long.
This one should equally work:
find . -name "*.log" -o -name "*.log.*" -mtime +7 -exec tar cvf test.tar {} +
Note the "+" at the end vs "\;".
For a reliable way when a very large number of files will match the search:
find . -name "*.log" -o -name "*.log.*" -mtime +7 > /tmp/find.out
tar cvf test.tar -I /tmp/find.out