New answers tagged

0

You could do: find . -type f -name '*.flac' -execdir sh -c ' if [ ! -d mp3 ] && [ ! -d Mp3 ]; then for file do ffmpeg -i "$file" -qscale:a 2 -map_metadata 0 -id3v2_version 3 "${file%.*}.mp3" done fi' sh {} + The idea being that with -execdir cmd {} +, (with some versions of GNU find), find will run cmd for all the matching file ...


3

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"


4

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


2

Unless you know what that sed command is doing you want to run one sed per input file. You can do this with either a for loop (if your shell supports recursive globbing) e.g. zsh, ksh93, yash, bash (tcsh and fish as well, but the loop syntax is different there). shopt -s globstar # bash #set -o globstar # ksh93 #set -o extended-glob # yash ...


-1

Assuming your file paths don't contain blanks, newlines, single quote, double quote or backslash characters: find . -name 'file.txt' -type f | xargs sed command


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 ...


1

Your loop is better written as find ... | while read -r file But then you need to make sure you quote the filename inside the loop. So we end up with find /path/to/directory -type f -name *.pdf | while read -r file do gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen -dNOPAUSE -dBATCH -dQUIET -sOutputFile="new_$file" "$file" rm "$...


0

Don't parse the output of ls. The shell expands /my.parent/my.folder* to the list of subdirectories of /my.parent whose name starts with my.folder in alphabetical order. Calling ls does nothing useful. To pick the last element of the list, if your shell has arrays, put the list in an array and pick the last element. directories=(/my.parent/my.folder*) ...


2

Without ls, in bash, or some other shell that has arrays (this should work even with names that have funny characters in them): $ names=( my.parent/my.folder* ); echo ${names[-1]} my.parent/my.folder2 Without arrays (not so robust against funny characters, but saves a fork of the ls): $ printf "%s\n" my.parent/my.folder* | tail -1 my.parent/my.folder2


3

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


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 -...


1

First, if you're going to be using a long path like that, it's best to give it its own variable name. Makes the code easier to read, makes sure you use the same name everywhere without typos, and makes it easier to change if you need. MAILBOX=/mnt/data/project_data/web_collab/mailbox Try this if there's less than a few hundred pdf files to contend with: ...


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 . -...


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' -...


1

for i in $(find . -type f -name "results.out); do grep -l "string1" $i ; exitcode=${?} if [ ${exitcode} -eq 0 ] # string1 is found in file $i then path=${i%/*} echo ${path} fi done


0

Assuming I understood correctly you want to do just that: find . -type f -name "results.out" -exec grep -l "string1" {} \; | xargs dirname First part gets matching filenames, then xargs passes those as an argument to dirname program which 'strips' filelame from path


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.


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....


1

With GNU find: find . -name '*.log' -printf '%p,%s\n' That will print the filename and the file's size in bytes, separated by a comma. Use %f instead of %p if you only want the file's basename (i.e. without the path). To display as kilobytes (units of 10^3, "KB") or kibibytes (units of 2^10, "KiB"), you'll need to post-process the output. See A ...


12

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


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 "$@"...


1

find . type -f ! -name '*.jpeg' ! -name '*.csv' -delete Read this as: traverse the current directory; when you find a file that is a regular file, and whose name does not match *.jpeg, and whose name is does not match *.csv, then delete it. If your version of find doesn't have -delete, make find invoke the rm command: replacte -delete by -exec rm {} +. ...


-1

ls -l will give you the all the data you need and more: : ls -l /var/log/*.log ... -rw-r--r-- 1 root root 123456 Jul 11 17:28 /var/log/xinetd.log ... Then you can extract the fields you need using awk: : ls -l /var/log/*.log | awk '{print $5,$9}' 123456 /var/log/xinetd.log If you want it separated with some other char: ...


0

This might help: ls -l --block-size=K *.log | awk {'print $9","$5'} > nameSize.csv


0

This would be my best guess and then put the contents in a file in your home directory called var-log.csv find . -type f -name "*.log" -exec ls -s {} \; > ~/var-log.csv


1

find . -name '.git' -prune is the same as find . -name '.git' -prune -print so it prunes and then prints find . -name '.git' -prune -o -name '*.md' -print is the same as find . \( -name '.git' -prune -true \) -o \( -name '*.md' -print \) so it does first clause, if it does prune then it does true (prune returns true), and does not do right hand ...


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 {} \;


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).


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


1

This sort of thing is easier to do in awk or perl than in a shell script (although if you're using a sh like bash which supports arrays, it's a bit easier than if using a sh without arrays. You still have far more complications with quoting and globbing or expansion where you don't want it in a shell script than you do in perl or awk) For example: #!/usr/...


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 ...


0

Solution: List files in target dir Replace to wildcard match with sed Pipe to rsync --exclude-from find target_dir | sed -r 's/\.\/(.+?)-.*/\1*/' | rsync --verbose --ignore-existing --exclude-from - src_dir/* target_dir


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; ...


1

A much better way to do this is: find . -name ".svn" -type d -prune -exec rm -rf '{}' '+' In case you have ".svn" inside another ".svn".


0

This solution will also work. The last part of the code (mv "$img" ./lowpixel) will move files below a specified width and height to a folder. In the following example all jpg images lower than 300x300 will be moved to a folder named lowpixel: find -iname \*.jpg | while read img; do anytopnm "$img" | pamfile | perl -ane 'exit 1 if $F[3]<300 || $F[5]<...


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,...


1

Your problem is that you have recursive symlinks. I would consider two options: forget about the -L, get all the files named .tex anywhere in the tree, and then filter them (is there no other criteria than being in a directory that is pointed to by a symlink that starts with "Math"?) Do it in two steps, both without -L: first you search for all symlinks ...


0

To strictly answer your question, you don't have duplicate files based on filename. If you want to keep the biggest files based upon the first part of the filename you can use the following awk script to get the names of files (with their sizes) which are not the largest based on prefix until the first underscore (a20160606, a20160607, etc.): find . -...


0

I don't now what is your use case, but I rarely have executable files under my web folder. And on production machines I also remove write permission to most of the files. For spacial cases i keep a separate file which include all the specific cases, like which folders need writing permission or a different owner, And use it to overwrite the defaults. find /...


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. ...


1

As an alternative, you can do this using only POSIX: find . -type f -name "*.jpg" -exec du -sk {} \; | awk 'BEGIN{total=0};{total += $1}; END{printf "%.3f MB\n", total / 1024}' Further reading: du - estimate file space usage (POSIX) find - find files (POSIX)


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 ...


1

This is a direct translation of the find-config algorithm in generic shell commands (tested under bash, ksh, and zsh), where I use a return code of 0 to mean success and 1 to mean NULL/failure. function findconfig { # from: https://www.npmjs.com/package/find-config#algorithm # 1. If X/file.ext exists, return it. STOP # 2. If X/.dir/file.ext exists, ...


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.


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 ...


0

Using cygwin, my version of rename does not have the regex replacement, nor does its rename [options] expression replacement file... syntax seem to work. Also, the bash suggestions fail with ... -c: line 0: unexpected EOF while looking for matching `"' ... -c: line 1: syntax error: unexpected end of file Alas... there is always perl: perl -MFile::Find -...


0

Another option - feed words one at a time to xargs for it to run grep against the file. xargs can itself be made to exit as soon as an invocation of grep returns failure by returning 255 to it (check the xargs documentation). Of course the spawning of shells and forking involved in this solution will likely slow it down significantly printf '%s\n' one two ...


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 ...



Top 50 recent answers are included