Hot answers tagged

14

Try: find . -name 'file.txt' -exec sed command {} + This finds all files named file.txt that in subdirectories of . and runs sed command against those files. If you want sed to modify those files in place, then add the -i option. Althought -exec ... + is now required by POSIX (hat tip: jordanm), some people may be using old versions of BSD find that do ...


13

Try: grep -rl --null --include '*.txt' LINUX/UNIX . | xargs -0r cp -t /path/to/dest Because this command uses NUL-separation, it is safe for all file names including those with difficult names that include blanks, tabs, or even newlines. The above requires GNU cp. For BSD/OSX, try: grep -rl --null --include '*.txt' LINUX/UNIX . | xargs -0 sh -c 'cp "$@"...


12

More portably (POSIX features only): find . -type f -name '*.txt' -exec grep -q LINUX/UNIX \; -exec cp {} /path/to/dest \;


12

awk 'FNR == 1 { f1=f2=f3=0; }; /one/ { f1++ }; /two/ { f2++ }; /three/ { f3++ }; f1 && f2 && f3 { print FILENAME; nextfile; }' * If you want to automatically handle gzipped files, either run this in a loop with zcat (slow and inefficient because you'll be forking awk many times in a loop, once ...


11

Set record separator to . so that awk will treat whole file as a one line: awk -v RS='.' '/one/&&/two/&&/three/{print FILENAME}' * Similarly with perl: perl -ln00e '/one/&&/two/&&/three/ && print $ARGV' *


6

Assuming your find supports it, use the -execdir option instead of -exec find * -name 'foo' -execdir pwd \; If it doesn't, please provide details of your platform and/or distribution (as appropriate).


5

Because when you use just *net* (without any quoting or escaping), it will be expanded by the shell as the (existing) net file/directory in the current directory before the find command run. So the command becomes: find . -name net As you can see it is just matching net, so usbnet.ko will not be matched. Also note that, without quoting and escaping, if ...


5

Using GNU find: #!/bin/sh dir='/mnt/data/project_data/web_collab/mailbox/' sr_today=$(find "$dir" \ ! -path '*/000000/*' \ -newermt '12am today' \ -ipath '*/sr_pdf/*.pdf' | wc -l) GNU find's -newermt option understands the same date formats as GNU date -d and touch -d. See man find and search for -...


5

You can do that with -maxdepth option /bin/find /root -maxdepth 1 -name '*.csv' Also, check out find command examples in SO documentation


4

This will go through your files and set the executable bit according to whether file believes that the file should be executable: find /var/www/html -type f -exec bash -c 'if file -b "$1" | grep -q executable; then chmod +x "$1"; else chmod -x "$1"; fi' None {} \; The find command is very similar to yours. The change is the addition of the bash commands. ...


4

find ./path/to/your/drive -type f -name '*.jpg' -exec du -ch {} + Or much faster find /path/to/your/drive -name "*.jpg" -print0 | du -ch --files0-from=- Or simply, du -ch /path/to/your/drive/*.jpg | grep total Or with help of awk, find /path/to/your/drive -iname "*.jpg" -ls | awk '{total += $7} END {print total}' On my system file size shows on ...


4

find . -depth -iname proj -type d -execdir mv {} test \; You need a find implementation with support for the non-standard -execdir predicate, but find implementations that support -iname generally also support -execdir in my experience.


4

If you have GNU find, you can print the path using the %h format specifier %h Leading directories of file's name (all but the last ele‐ ment). If the file name contains no slashes (since it is in the current directory) the %h specifier expands to ".". So for example you could do find . -name 'results.out' -...


4

With non-GNU find: find /root ! -path /root -prune -type f -name "*.csv" This will prune (remove) all directories in /root from the search, except for the /root directory itself, and continue with printing the filenames of any file that matches *.csv. With GNU find: find -maxdepth 1 /root -name "*.csv"


3

The error is occurring because find doesn't know when to stop. If you run find | head, when head gets its ten lines and exits, the next time find tries to write a filename, it'll get a SIGPIPE (letting it know that the other end of the pipe is broken or closed), and find will gracefully exit. But here, find isn't writing anything, ls is. find can see that ...


3

A simple loop of checking the current directory and if it's not found then strip off the last component would work #!/bin/bash wantfile="$1" dir=$(realpath .) found="" while [ -z "$found" -a -n "$dir" ] do if [ -e "$dir/$wantfile" ] then found="$dir/$wantfile" fi dir=${dir%/*} done if [ -z "$found" ] then echo Can not find: $wantfile else ...


3

For compressed files, you could loop over each file and decompress first. Then, with a slightly modified version of the other answers, you can do: for f in *; do zcat -f "$f" | perl -ln00e '/one/&&/two/&&/three/ && exit(0); }{ exit(1)' && printf '%s\n' "$f" done The Perl script will exit with 0 status (success)...


3

The following sh/Bash one liner is another method, though will only work in the current directory, and doesn't recurse: for f in ./*.txt; do if grep -l 'LINUX/UNIX' "$f"; then cp "$f" /path/to/dest/; fi; done The -l option to grep will print a list of the files which are being copied, though you could use -q if you don't want to see anything on the screen....


3

To get ls to display the folder name instead of listing its contents, use its -d argument such as: ls -ld ~


2

I think this must be one of the silliest command piplines I ever have concocted: $ find . -type l -name "Math*" -print0 | xargs -0 -n 1 -IXXX find XXX/ -type f -name "*.tex" -print0 | xargs -0 fgrep "word" Find all symbolic links called Math*. Do find again on each found path, looking for *.tex files. The xargs need to use -n 1 to call find with no ...


2

Of all the solutions proposed so far, my original solution using grep is the fastest one, finishing in 25 seconds. It's drawback is that it's tedious to add and remove keywords. So I came up with a script (dubbed multi) that simulates the behavior, but allows to change the syntax: #!/bin/bash # Usage: multi [z]grep PATTERNS -- FILES command=$1 # first ...


2

Assuming your files have sane names (i.e. they don't have embedded newlines), something like this should work: find . -mtime +60 | fgrep -v -x -f exceptions.txt | xargs -d '\n' rm -f Replace rm -f with ls -1 for a dry run first. Put paths exactly as they are printed by find in exceptions.txt.


2

You can collect the names into a variable fns and echo this at the end. Since you have a pipe you need to keep the variable in the same subshell as the while/do/done. ${fns:1} is a substring of the variable, dropping the initial extra comma. #!/bin/bash PATHX="/path/to/my/files" find "${PATHX}" -maxdepth 1 -type f -name "*.csv" | ( fns= while read d; ...


2

I would do it in two find calls: One to find all zip files and then process them Another to deal with the regular files This is a little cumbersome, the complicated part is the awk call. It processes the output of unzip -l which is not very script friendly. It searches for lines starting with numbers (to get rid of the headers), gets rid of empty lines,...


2

One way to do it: #! /bin/sh dir=$(pwd -P) while [ -n "$dir" -a ! -f "$dir/$1" ]; do dir=${dir%/*} done if [ -f "$dir/$1" ]; then printf '%s\n' "$dir/$1"; fi Replace pwd -P by pwd -L if you want to follow symlinks instead of checking physical directories.


2

With your version of find, the {} in the string is replaced by the file name. It is almost always an error to use {} as part of a string, because the file name is inserted just like that. Here, the file name is used as a shell script fragment. If there's a directory called a'$(touch wibble)' then your command executes the shell code pwd; echo 'a'$(touch ...


2

Change it like this: find pictures -type d -links 2 -execdir \ sh -c 'pwd; echo "$1"; zip -vr "$1/$1.zip" "$1" -x \*.zip -x \*.id' sh {} \;


2

If your files have EXIF data that includes the creation date and time, then you can use exiftool to list just the hour and filename, and filter on that: find . -name '*jpg' -exec exiftool -q -d '%H' -p '$CreateDate $filename' \; 2>/dev/null | awk '$1>=6 && $1<18 {$1=""; print}' Beware, check if the date/time in the files is in local time ...


2

this command worked for me find . -iname "*pg" -printf '%Tc %p\n' | grep "\ 08:\|\ 07:\| \06:" it is nonetheless the files' unix timestamps not the exif data timestamp which is used for the search and I am unsure about performance, but I gave this answer as you indicated findutils as a tag


2

With zsh: print -rl ./**/results.out(.e_'grep -q string $REPLY'_:h) this searches recursively for regular files (.) named results.out, runs grep -q ... on each of them and if that evaluates true it prints only the head of the path (the path without the last element). Another way with find and sh, using parameter expansion to extract the head: find . -...



Only top voted, non community-wiki answers of a minimum length are eligible