ZSH find with multiple operators - zsh

If I want to search for a directory or symlinks, I can do the following in bash:
find . \( -type d -o -type l \) -maxdepth 1
The same command doesn't work in ZSH:
find: missing argument to `-type'
): No such file or directory
-maxdepth: No such file or directory
1: No such file or directory
So I guess some more escaping or similar is needed. Is there a way to specify this command so that it works in both - ZSH and bash?
Here the output of setopt in ZSH:
alwaystoend
autocd
autopushd
nobeep
nocheckjobs
completeinword
extendedhistory
noflowcontrol
histexpiredupsfirst
histfindnodups
histignorealldups
histignoredups
histignorespace
histsavenodups
histverify
nohup
incappendhistory
interactive
interactivecomments
longlistjobs
monitor
promptsubst
pushdignoredups
pushdminus
sharehistory
shinstdin
zle
My ZSH version is: zsh 5.8 (x86_64-ubuntu-linux-gnu)
Of course, I'm using Oh My Zsh.

The OP confirmed that a global alias for l was defined, which altered the set of arguments seen by find, leading to the error.

Related

rsync with find in the files-from

I'm trying to transfer files which have been updated in the 31 days. I'm trying to run
/bin/rsync --remove-source-files --files-from="<(find /tmp/rsync/source -type f -mtime -31 -print0)" /tmp/rsync/source /tmp/rsync/destination
However when trying this, i keep receiving the following error:
failed to open files-from file <(find /tmp/rsync/source -type f -mtime -31 -print0): No such file or directory
The directory exists and is accessible.
This is the output of the find:
$ find /tmp/rsync/source -type f -mtime -31
/tmp/rsync/source/testfile2.txt
/tmp/rsync/source/testfile.txt
/tmp/rsync/source/sourcefile.txt
/bin/rsync --remove-source-files --files-from="<(find /tmp/rsync/source -type f -mtime -31 -print0)" /tmp/rsync/source /tmp/rsync/destination
<( ... ) is Bash Shell Process Substitution - https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html.
So:
You have to be using Bash, i.e. as your command-line shell if running interactively, or as the shell script interpreter, if running in a shell script.
If not using Bash on command-line nor in a shell script or if you are passing that command to something that is executing the rsync program directly and not through a Bash shell, it won't work.
You can't put that syntax inside double quotes as shown. When quoted like that, Bash interprets it as a filename (thus the error).
It's unfortunate this this is coming up first in search results when other questions about this have much better answers.
I've seen people use the --files-from="<(...)" notation several places, but I don't see any reference to it in the rsync manual. Maybe it's a special shell syntax for some people? or distro-added feature? I get the same error message as above when I try to use that notation.
The official way to do it is to either write your list of files into a real file:
find . -mtime -7 > past_week.txt
rsync --files-from=past_week.txt SRC/ DST/
or to pipe the list of files on stdin:
find . -mtime -7 | rsync --files-from=- SRC/ DST/
The single dash - filename here means STDIN, and this is a common convention among unix tools.
If you are worried about files with newlines in the file name, you should use NUL delimiters for the list. (but beware this will also affect --include-from and --exclude-from)
find . -mtime -7 -print0 | rsync -0 --files-from=- SRC/ DST/
You can also list out the files on the command line like rsync -av `find . -mtime -7` DST/ but that doesn't preserve their hierarchy in the tree, and if you have more than a few files that will create a massive command line, and may fail to execute if it exceeds the limit of the operating system.
Tried, this works good for me
find /tmp/rsync/source -type f -mtime -31 | rsync -rcvh /tmp/rsync/source/ /tmp/rsync/destination/ --dry-run
Remove dry-run for actual execution
Since i was forced to use a pre-existing script which was parsing on "{" brackets and where you couldn't run commands before the rsync script i was unable to use the above mentioned solutions.
However i was able to use the following to get it working:
/bin/rsync --recursive --remove-source-files `find /tmp/rsync/source -type f -mtime -31` /tmp/rsync/destination

Recursively execute latexmk -c on folders

I'd like to execute the command latexmk -c on all of the directories inside a directory, e.g.,
.
./test-dir
The effect is to remove all of the auxiliary files created curing latex compilation.
I've tried using the find command to tell me about the directories and then execute a command, like so:
find -type d -exec latexmk -c \;
But unfortunately, that command only has the effect of removing the auxiliary files in the directory in which I call it, not in the subdirectory (test-dir in this example).
I had a very similar problem: I wanted to convert .tex files recursively. The final solution for me was:
find . -name '*.tex' -execdir latexmk -pdf {} \;
The secret here is to use -execdir instead of -exec which executes the command in the directory the file was found. So the solution to your problem is most likely:
find -type d -execdir latexmk -c \;

Omit "Is a directory" results while using find command in Unix

I use the following command to find a string recursively within a directory structure.
find . -exec grep -l samplestring {} \;
But when I run the command within a large directory structure, there will be a long list of
grep: ./xxxx/xxxxx_yy/eee: Is a directory
grep: ./xxxx/xxxxx_yy/eee/local: Is a directory
grep: ./xxxx/xxxxx_yy/eee/lib: Is a directory
I want to omit those above results. And just get the file name with the string displayed. can someone help?
grep -s or grep --no-messages
It is worth reading the portability notes in the GNU grep documentation if you are hoping to use this code multiple places, though:
-s
--no-messages
Suppress error messages about nonexistent or unreadable files. Portability note: unlike GNU grep, 7th Edition Unix grep did not conform to POSIX, because it lacked -q and its -s option behaved like GNU grep’s -q option.1 USG-style grep also lacked -q but its -s option behaved like GNU grep’s. Portable shell scripts should avoid both -q and -s and should redirect standard and error output to /dev/null instead. (-s is specified by POSIX.)
Whenever you are saying find ., the utility is going to return all the elements within your current directory structure: files, directories, links...
If you just want to find files, just say so!
find . -type f -exec grep -l samplestring {} \;
# ^^^^^^^
However, you may want to find all files containing a string saying:
grep -lR "samplestring"
Exclude directory warnings in grep with the --exclude-dir option:
grep --exclude-dir=* 'search-term' *
Just look at the grep --help page:
--exclude-dir=PATTERN directories that match PATTERN will be skipped.

Shell script question

I want to execute following command in shell script
cp /somedire/*.(txt|xml|xsd) /destination/dir/
But this does not run inside shell script. Any quick help?
createjob.sh: line 11: syntax error near unexpected token `('
My shell is zsh.
Thanks
Nayn
Your use of parentheses and alternation is a zsh-specific construct. It doesn't work in other shells, including zsh in sh compatibility mode.
If you want to keep using this construct, you'll have to invoke zsh as zsh (presumably by replacing #!/bin/sh by #!/bin/zsh or something like that).
If you need your script to run on ksh, use #!/bin/ksh or #!/usr/bin/env ksh and
cp /somedire/*.#(txt|xml|xsd) /destination/dir/
If you also need to support bash, that same command with the # will work provided you run the following commands first:
shopt -s extglob 2>/dev/null ## tell bash to parse ksh globbing extensions
setopt ksh_glob 2>/dev/null ## tell zsh to parse ksh globbing extensions
If you need POSIX sh compatibility, you'll have to use three separate commands, and prepare for an error message if any of the three extensions has no match. A more robust solution would use find:
find /somedire -name /somedire -o -type d -prune -o \
\( -name '*.txt' -o -name '*.xml' -o '*.xsd' \) \
-exec sh -c 'cp "$#" "$0"' /destination/dir {} +
No idea about zsh but Bash doesn’t know about regular expressions in paths, only wildcards.
You can try using find:
find -E . -regex '.*\.(txt|xml|xsd)' -exec cp {} /destination/dir \;
Have a look at the manpage for an explanation of the syntax of find.
This would work in bash, and probably zsh as well: cp /somedire/*.{txt,xml,xsd} /destination/dir/
It's not in POSIX, though, so it won't work with most /bin/sh's.

Unix - how to source multiple shell scripts in a directory?

when I want to execute some shell script in Unix (and let's say that I am in the directory where the script is), I just type:
./someShellScript.sh
and when I want to "source" it (e.g. run it in the current shell, NOT in a new shell), I just type the same command just with the "." (or with the "source" command equivalent) before it:
. ./someShellScript.sh
And now the tricky part. When I want to execute "multiple" shell scripts (let's say all the files with .sh suffix) in the current directory, I type:
find . -type f -name *.sh -exec {} \;
but "what command should I use to "SOURCE" multiple shell scripts in a directory"?
I tried this so far but it DIDN'T work:
find . -type f -name *.sh -exec . {} \;
and it only threw this error:
find: `.': Permission denied
Thanks.
for file in *.sh
do . $file
done
Try the following version of Jonathan's code:
export IFSbak = $IFS;export IFS="\0"
for file in `find . -type f -name '*.sh' -print0`
do source "$file"
end
export IFS=$IFSbak
The problem lies in the way shell's work, and that '.' itself is not a command (neither is source in this). When you run find, the shell will fork itself and then turn into the find process, meaning that any environment variables or other environmental stuff goes into the find process (or, more likely, find forks itself for new processes for each exec).
Also, note that your command (and Jonathan's) will not work if there are spaces in the file names.
You can use find and xargs to do this:
find . -type f -name "*.sh" | xargs -I sh {}

Resources