Tag Info

New answers tagged

0

Since it's a static structure you can directly address the filenames and let the shell copy them: for f in conf/widget*/env2.cfg do cp "${f}" "${f/env2/env3}" done


2

Try find . -name env2.cfg -execdir cp {} env3.cfg \; By replacing exec to execdir. It will mean that your 'action' (cp) will take place inside the folder where the file (env2.cfg) have been found.


2

Your example copies all env2.cfg files to the current working directory, which is ".". You need to give the file found a new name, including its directory. This one worked for me: for file in $(find . -name env2.cfg); do backup=$(echo $file | sed 's/env2.cfg/env3.cfg/'); cp $file $backup; done edit: Or the even more elegant way, so you don't have to ...


1

Welcome to Unix :) To answer some of your minor questions that answers to the main question didn't cover: Shell scripting certainly has some rough edges, since a lot of things break on file names with spaces. And almost everything breaks on filenames with newlines (fortunately, nobody makes those on purpose). Filenames containing glob characters like [, ...


5

For your -name version, instead of ! -name '*[done]*' you need ! -name '*\[done\]*' - otherwise it's taking the letters in brackets as a character set, and thus excluding anything that includes the letter "d" or "o" or "n" or "e" (and all of your filenames contain "e"). You were then negating that condition a second time, so that rather than excluding all ...


5

You've got this code: for file in *.mkv *avi *mp4 *flv *ogg *mov; do target="${file%.*}.mkv" ffmpeg -i "$file" "$target" && rm -rf "$file" done which runs in the current directory. To turn it into a recursive process you have a couple of choices. The easiest (IMO) is to use find as you suggested. The syntax for find is very "un-UNIX-like" ...


3

Example snippet without piping (assumes you are giving the path as argument): #!/bin/bash backup_dir=/backup/ OIFS="$IFS" IFS=$'\n' files="$(find "$1" -type f -name '*.mkv' -or -name '*.avi' -or -name '*.mp4' -or -name '*.ogg' -or -name '*.mov' -or -name '*.flv')" for f in $files; do # get path d="${f%/*}" # get filename b="$(basename ...


3

With POSIX find: find . \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o \ -name '*ogg' -o -name '*mov' \) -exec sh -c ' for file do target="${file%.*}.mkv" echo ffmpeg -i "$file" "$target" done' sh {} + Replace echo with whatever command you want to use. If you have GNU find or BSD find, you can use -regex: find ...


2

You don't need to recursively enumerate directories to delete them with rm -rf; you can simply list the top-level directories you want to delete. To determine whether a directory entry is a directory rather than a file, you can use find's -type d test; using . isn't a good indicator. The following should work for you: find * -maxdepth 0 ! -name encoded ...


3

Because you told it to scan starting at / (root). It does what you tell it to do.


4

Some thoughts, almost all of which include -prune (why do you want to avoid this?): If you have a consistent and known set of local filesystem types, use something like find / \( -fstype rootfs -o -fstype ext4 -o -prune \) ...others... -print If you have a known set of pseudo-filesystem types, use something like find / \( -fstype tmpfs -o -fstype udev \) ...


6

Try this: touch -d"April 13 3 AM" file1 touch -d"April 13 9 AM" file2 find . -newer file1 ! -newer file2 -exec grep -l "pcV6URY" {} + rm file1 file2 How it works find can work directly with times but touch handles human-style dates better: touch -d"April 13 3 AM" file1; touch -d"April 13 9 AM" file2 This creates two files to mark the beginning and end ...


2

If you don't mind using two different commands for the file names and content, the below commands will help you. find /sys -name "*filesystem*" The above command will find all the files/directories with "filesystem" as part of the filename/directoryname. grep -rn "filesystem" /sys/* The above command will look for all the files containing "filesystem" ...


1

find . -name '*.jpg' -exec sh -c ' for file do if identify "$file"; then printf "%s\n" "$file" >&3 else printf "%s\n" "$file" >&4 fi done' sh {} + 3> good 4> bad > /dev/null 2>&1


2

This finds malformed images and stores their names in names.txt: find -name '*.jpg' -exec bash -c 'identify "$1" &>/dev/null || echo "$1">>names.txt' none {} \; How it works find -name '*.jpg' This starts up find as usual. -exec bash -c 'identify "$1" &>/dev/null || echo "$1" >names.txt' none {} \; This runs identify on each ...


23

That's a really nice catch. From a quick look at the source code for GNU find, I would say this boils down to how fnmatch behaves on invalid byte sequences (pred_name_common in pred.c): b = fnmatch (str, base, flags) == 0; (...) return b; This code tests the return value of fnmatch for equality with 0, but does not check for errors; this results in any ...


12

find -name option uses shell pattern matching notation to perform matching filename. * is a pattern matching multiple characters, shall match a string of zero or more characters. find uses fnmatch to check pattern matching, so you can use ltrace to check the result: $ touch $'\U1212'aa $ touch D$'\351'sinstaller $ LC_ALL=en_US.utf8 ltrace -e fnmatch find ...


0

Why not try this: find / -name '*f*' -printf "%h\n" | sort -u


1

It's the same. You just have to provide the parent directory rather than the prefix of files. In your example, it would be: find /path/to -type f -mtime +5 -exec rm {} \; This will delete all the files older than 5 days which are under /path/to and its sub-directories. To delete empty sub-directories, refer to @Costas comment above.


1

If your find command has the -maxdepth option (Linux (GNU or BusyBox), FreeBSD, NetBSD, OSX): find /var/www -maxdepth 3 -name wp-cron.php -exec php -q "{}" \; If you want to run wp-cron.php files at an exact depth, you can use wildcards: for x in /var/www/*/*/wp-cron.php; do php -q wp-cron.php done You can run locate wp-cron.php to quickly list ...


1

Assuming .DS_Store represent files and not directories, the most portable still fast way to do it would be: sudo find / -name .DS_Store -exec rm {} + The only risk is for sudo not to be available but it is quite low nowadays. -delete demand GNU find which is not always present. The command termination + instead of \; highly optimizes the exec clause by ...


0

For a machine such as your macbook you won't find much difference in performance between the two commands. However, if you look at the -exec version you can see a subtle difference: sudo find / -iname ".file-to-delete" -exec rm {} \; This means that you will find all those files with name ".file-to-delete". However this search might return some unwanted ...


0

This answer is shamelessly based on slm answer. It was an interesting approach, but it has a limitation if the file and/or directory names had special chars (space, semi-column...). A good habit is to use find /somewhere -print0 | xargs -0 someprogam. Sample data For the examples below we'll use the following data mkdir -p dir{1..3}/dir\ {100..112} touch ...


1

You could write ls -d */ | while read dir; do echo "$dir: $(ls $dir | wc -l)" done When the filenames are numbered without leading zeroes, you can try ls -d */ | cut -d/ -f1 | while read dir; do COUNT=$(ls $dir | wc -l); echo "$dir: ${COUNT}" ; if [ ${COUNT} -gt 10000 ]; then ls ${dir}/${dir}?????*.ext | grep -v ...


3

find . -name PKA.dump -type f -exec awk ' FNR == 20 {print; nextfile}' {} + nextfile, where available (GNU awk and some others like FreeBSD's and recent versions of mawk and soon to be added to the standard) will skip to the next file. Where not, it will be ignored (it's just like dereferencing a nextfile variable); it will still work but read the files ...


2

Use single quotes instead of double quotes, so that backticks and $ don't get interpreted by the original shell: find . -maxdepth 1 -type d -name 'acer' -exec sh -c 'echo {} $(ls {} | wc -l)' \; For the second question, I would put what you want to do into a separate script, that takes the directory name as an argument. Then do: find . -maxdepth 1 -type ...


1

To use multiple statements, such as a for-loop, as the argument to -exec, one needs to invoke a shell, such as bash, explicitly: find .. -name bin -exec bash -c 'for file in "$1"/* ; do echo "$file" ; done' none {} \; This is safe even for filenames that contain spaces or other hostile characters. How bash -c works One can invoke bash with a command of ...


0

It looks like you've got things reversed from what you want. Try this: for f in `find .. -name bin` do echo $f done


1

grep "1234-5678" * -r or grep "1234-5678" * -R if you want to follow the symbolic links.


2

Portably/standardly: find . -type f -exec grep 1234-5678 /dev/null {} + Some grep implementations have -r or -R options to search in files recursively. The behaviour varies from implementation to implementation though. With the grep found in AIX 6.1 for instance, you'll probably want to use the -R option1. Beware though that contrary to the find ...


5

With GNU xargs: ack -l --print0 foo | xargs -r0 rm -- ack's --print0 and xargs' -0 cause ack and xargs to write and read using NUL as the delimiter, which guarantees proper filename handling. Without it, xargs will accept a far more wide range of characters as a delimiter.


1

You can use option -l with grep and ack which lists only filename: grep -l --null foo ./* | xargs -r0 rm or: ack -l --print0 foo ./* | xargs -r0 rm --


3

To exclude specific paths, on Linux: find / -path /sys -prune -o -path /proc -prune -o -type d Another approach is to tell find not to recurse under different filesystems. find / -xdev -type d You could also use locate to query a database of file names (usually updated nightly) instead of the live system. locate '*' | shuf -n 1


2

rm's stdin (where it reads the prompt answer from) is /dev/null (set by GNU xargs, some other xargs implementations would keep it as the pipe from ls). Your sh is getting many arguments at once, but you're only processing one ($1). Also note that the newline character is as valid as any in a file name which is why you generally can't process the output of ...


1

After your last backup using the old disk, create a timestamp and then backup files after that timestamp. You'll use find and the --newer flag to list files newer than the timestamp, then the --file-from flag to rsync to specify that list of files for copying. Here's how: Step 1: Before you swap to the new drive: touch /someplace/timestamp.txt Step 2: ...


1

On Linux, you can use cp --backup=numbered to obtain files called foofile.log, foofile.log.~1~, foofile.log.~2~, etc. find /location -name '*file.log' -exec cp --backup=numbered {} /location \; If you want to include the original directory as part of the target file name, you can replace the slashes by some other string. However, there are a couple of ...


1

presumably you have ssh access to the server? so either: grant the server ssh access to your local machine and then use something like ssh you@server 'find ... | xargs -0 -I {} rsync "{}" you@yourmachine:/media/dir' or: create a list of files on the server using find, then copy this file to your local machine and run rsync from there to grab these files ...


2

I assume that the files under myfiles are not symbolic links, and that none of the file names contain newlines. (My approach can still work if these assumptions are violated but it gets more complicated.) I also assume that you have the common readlink utility and that it supports -f to canonicalize paths, which is the case on Linux (both GNU and BusyBox), ...


4

If I undersood the question correctly you need files in myfiles which do not have symlinks in images: #!/bin/bash OIFS="$IFS" IFS=$'\n' files="$(find myfiles/ -type f -name '*.jpg' -or -name '*.cr2')" for f in $files; do list="$(find -L images/ -xtype l -samefile "$f")" if [[ "$list" == "" ]]; then echo "$f does not have symlink." fi ...


3

With zsh: print -rl ${(0)^"$(locate -0 ...)"}(N.) (0) is a parameter expansion flag that splits on NUL characters (as we use locate -0), short for (ps:\0:). With ^, instead of adding (N.) at the end of the array, we add it to each element. (N.) is a glob qualifier, . to match only regular files, N to remove the element if it doesn't match (doesn't exist ...


0

xargs will repeat command for each line if you specify -L 1 or -i parameter. See here $ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i bash -c '(test -d "{}" && echo "{}")' Admittedly, it is kicking of a new shell for each file, but it does have the benefit of being nice and compact. EDIT: I wasn't quite happy with that answer ...


2

I'd use this kind of construct as a starting point find / -type d -print0 | xargs -0 -I'{}' sh -c 'ls -ltr {} | tail -1' Caveat: it doesn't like empty directories (total 0 is output).


2

Probably this would be better: combination of find and shell find / -type d -print0 | while read -r -d '' dir; do ls -ltr "$dir" | sed '$!d' done find will output each directory found, using the null byte instead of a newline to separate them. This stream is fed into a while loop, using read -d '' to extract each null-delimited directory name. Then, ...


0

My two cents: while IFS= read i; \ do \ if [ -f "$i" ]; \ then \ echo "$i"; \ fi; \ done < <(locate --regex --basename "xfce4-keyboard-overlay$") This is more or less the way G-Man did it combined with process substitution.


3

This is about as inelegant as the other answers, but maybe less inefficient: locate --regex --basename "xfce4-keyboard-overlay$" | while IFS= read -r f; do [ -f "$f" ] && printf "%s\n" "$f"; done (broken into two lines for readability).  The above will handle names containing spaces.  The IFS= seems to be necessary to handle names with ...


-1

What if you combine locate with file and grep?... $ for f in `locate --regex --basename "xfce4-keyboard-overlay$"`; do file $f; done | grep -vi directory


1

locate --null --regex --basename "xfce4-keyboard-overlay$" | xargs -r0 sh -c 'find "$@" -prune ! -type d' sh


1

You're misusing find and it's working accidentally. You see - by running: find *.txt What you're actually doing is invoking: find lect1.txt And you're accomplishing little more than echo. If however, you run find with a search criteria - so for example: find . -name '*.txt' Then it will traverse the current directory . and print any filename ...



Top 50 recent answers are included