I would like to do something like:
find ./ -name "*.jpg" -nbresult 50 -exec cp {} /50randomsjpgfrommyharddrive
I can use head and xargs, but with -print0, head doesn't work any more.
Provided that you do not have newlines in the filenames, -print0 is unnecessary, and you can have instead:
find ./ -name "*.jpg" | head -n 50 | xargs -d'\n' -n1 -I'{}' cp '{}' /50randomsjpgfrommyharddrive
In this command, the -d'\n' will make xargs delimit on newlines. Other whitespace in filenames, which would by default be treated as a delimiter by xargs, are then not a problem.
Alternatively, if you still need to use -print0, the following command line incorporates a filter which is analogous to head -n 50 but is based on null delimiters (rather than newlines) on its input and output. Note that -0 is needed on xargs in this case.
find ./ -name "*.jpg" -print0 | perl -p0e 'exit if $i++ == 50' | xargs -0 -n1 -I'{}' cp '{}' /50randomsjpgfrommyharddrive
GNU head has an option called -z for changing the line terminator to NUL, which can be used for this task as shown below.
find -name '*.jpg' -print0 \
| head -z -n 50 \
| xargs -0 cp -t /destination
Related
Can anyone see why Perl deletes all file content when it is used together with find?
echo stning >> test.tex
echo stning >> test.tex
find . -type f -name \*.tex -print0 | xargs -0 perl -i -ne 's/stning/sætning/g'
cat test.tex
The last command doesn't return anything, and that the issue.
You need -p, not -n. The -n flag only reads, but doesn't print.
find . -type f -name \*.tex -print0 | xargs -0 perl -i -pe 's/stning/sætning/g'
You can easily remember this with the mnemonic perl pie, which is perl -p -i -e or shorter perl -pi -e.
You can achieve your result with sed itself. As just you need an inline replacement.
find . -type f -name \*.tex -print0 | xargs -0 sed -i 's/stning/sætning/g'
find . -name "data.txt" -print0 | grep -rl "pa028" ./ |xargs -0 sed -i '' -e 's/pa028/pa014/g'
I tried to replace pa028 with pa014 in the file name "data.txt" in all subdirectories. Can you find please correct me?
You can't put grep between find -print0 and xargs -0 because grep operates on lines, and this pipeline contains null-separated text instead of lines. Additionally, grep -r . will ignore the standard input you so expensively set up find to produce.
find . -name "data.txt" -exec grep -q "pa028" {} \; -print0 |
xargs -r -0 sed -i '' -e 's/pa028/pa014/g'
The logic here is to use -exec grep -q as a predicate to find so we produce a null-terminated list of matching files (for which the -exec returns true) to pass to xargs -r -0. (The -r option is important, too; you get weird errors if xargs runs anyway even though find produced no output.)
There is an extension to GNU grep to operate on null-terminated strings with -z and print null-terminated file names with -Z -l but that's a fairly recent development, so I'm not yet prepared to recommend that.
I want to run a command, that will count the number of words in all file. (From the selected number of files)
If i do like, find ABG-Development/ -name "*.php" | grep "<?" | wc -l , it will search only in the filename not the file contents.
And I tried one more way like
find ABG-Development/ -name "*.php" -exec grep "<?" {} \; | wc -l, I got error.
In above example I need how many time "
Please help..
use xargs
find ABG-Development/ -name "*.php" -print0 | xargs -0 grep "<?" | wc -l
I'm using find to all files in directory, so I get a list of paths. However, I need only file names. i.e. I get ./dir1/dir2/file.txt and I want to get file.txt
In GNU find you can use -printf parameter for that, e.g.:
find /dir1 -type f -printf "%f\n"
If your find doesn't have a -printf option you can also use basename:
find ./dir1 -type f -exec basename {} \;
Use -execdir which automatically holds the current file in {}, for example:
find . -type f -execdir echo '{}' ';'
You can also use $PWD instead of . (on some systems it won't produce an extra dot in the front).
If you still got an extra dot, alternatively you can run:
find . -type f -execdir basename '{}' ';'
-execdir utility [argument ...] ;
The -execdir primary is identical to the -exec primary with the exception that utility will be executed from the directory that holds the current file.
When used + instead of ;, then {} is replaced with as many pathnames as possible for each invocation of utility. In other words, it'll print all filenames in one line.
If you are using GNU find
find . -type f -printf "%f\n"
Or you can use a programming language such as Ruby(1.9+)
$ ruby -e 'Dir["**/*"].each{|x| puts File.basename(x)}'
If you fancy a bash (at least 4) solution
shopt -s globstar
for file in **; do echo ${file##*/}; done
If you want to run some action against the filename only, using basename can be tough.
For example this:
find ~/clang+llvm-3.3/bin/ -type f -exec echo basename {} \;
will just echo basename /my/found/path. Not what we want if we want to execute on the filename.
But you can then xargs the output. for example to kill the files in a dir based on names in another dir:
cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm
On mac (BSD find) use:
find /dir1 -type f -exec basename {} \;
As others have pointed out, you can combine find and basename, but by default the basename program will only operate on one path at a time, so the executable will have to be launched once for each path (using either find ... -exec or find ... | xargs -n 1), which may potentially be slow.
If you use the -a option on basename, then it can accept multiple filenames in a single invocation, which means that you can then use xargs without the -n 1, to group the paths together into a far smaller number of invocations of basename, which should be more efficient.
Example:
find /dir1 -type f -print0 | xargs -0 basename -a
Here I've included the -print0 and -0 (which should be used together), in order to cope with any whitespace inside the names of files and directories.
Here is a timing comparison, between the xargs basename -a and xargs -n1 basename versions. (For sake of a like-with-like comparison, the timings reported here are after an initial dummy run, so that they are both done after the file metadata has already been copied to I/O cache.) I have piped the output to cksum in both cases, just to demonstrate that the output is independent of the method used.
$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 basename -a | cksum'
2532163462 546663
real 0m0.063s
user 0m0.058s
sys 0m0.040s
$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 -n 1 basename | cksum'
2532163462 546663
real 0m14.504s
user 0m12.474s
sys 0m3.109s
As you can see, it really is substantially faster to avoid launching basename every time.
Honestly basename and dirname solutions are easier, but you can also check this out :
find . -type f | grep -oP "[^/]*$"
or
find . -type f | rev | cut -d '/' -f1 | rev
or
find . -type f | sed "s/.*\///"
-exec and -execdir are slow, xargs is king.
$ alias f='time find /Applications -name "*.app" -type d -maxdepth 5'; \
f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l; \
f -print0 | xargs -0 basename | wc -l
139
0m01.17s real 0m00.20s user 0m00.93s system
139
0m01.16s real 0m00.20s user 0m00.92s system
139
0m01.05s real 0m00.17s user 0m00.85s system
139
0m00.93s real 0m00.17s user 0m00.85s system
139
0m00.88s real 0m00.12s user 0m00.75s system
xargs's parallelism also helps.
Funnily enough i cannot explain the last case of xargs without -n1.
It gives the correct result and it's the fastest ¯\_(ツ)_/¯
(basename takes only 1 path argument but xargs will send them all (actually 5000) without -n1. does not work on linux and openbsd, only macOS...)
Some bigger numbers from a linux system to see how -execdir helps, but still much slower than a parallel xargs:
$ alias f='time find /usr/ -maxdepth 5 -type d'
$ f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l
2358
3.63s real 0.10s user 0.41s system
2358
1.53s real 0.05s user 0.31s system
2358
1.30s real 0.03s user 0.21s system
2358
0.41s real 0.03s user 0.25s system
I've found a solution (on makandracards page), that gives just the newest file name:
ls -1tr * | tail -1
(thanks goes to Arne Hartherz)
I used it for cp:
cp $(ls -1tr * | tail -1) /tmp/
find . -name "*.php" | xargs grep -i -n "searchstring" >output.txt
Here I am trying to write data into a file which is not happening...
How about appending results using >>?
find . -name "*.php" | xargs grep -i -n "searchstring" >> output.txt
I haven't got a Linux box with me right now, so I'll try to improvize.
the xargs grep -i -n "searchstring" bothers me a bit.
Perhaps you meant xargs -I {} grep -i "searchstring" {}, or just xargs grep -i "searchstring"?
Since -n as grep's argument will give you only number lines, I doubt this is what you needed.
This way, your final code would be
find . -name "*.php" | xargs grep -i "searchstring" >> output.txt
find . -name "*.php" -exec grep -i -n "function" {} \; >output.txt
But you won't know what file it came from. You might want:
find . -name "*.php" -exec grep -i -Hn "function" {} \; >output.txt
instead.
I guess that you have spaces in the php filenames. If you hand them to grep through xargs in the way that you do, the names get split into parts and grep interprets those parts as filenames which it then cannot find.
There is a solution for that. find has a -print0 option that instructs find to separate results by a NUL byte and xargs has a -0 option that instructs xargs to expect a NUL byte as separator. Using those you get:
find . -name "*.php" -print0 | xargs -0 grep -i -n "searchstring" > output.txt
Try using line-buffered
grep --line-buffered
[edit]
I ran your original command on my box and it seems to work fine, so I'm not sure anymore.
Looks fine to me. What happens if you remove >output.txt?
If you're searching trees of source code, please consider using ack. To do what you're doing in ack, regardless of there being spaces in filenames, you'd do:
ack --php -i searchstring > output.txt
I always use the following command. It displays the output on a console and also creates the file
grep -r "string to be searched" . 2>&1 | tee /your/path/to/file/filename.txt
Check free disk space by
$ df -Th
It could be not enough free space on your disk.