Find and replace text in files with xonsh - xonsh

How would one replace text in all files within a given directory (recursively) while in the xonsh shell?
For example, how would I rewrite the below bash command:
find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +

I might try this:
for fpath in pg`*baz*`:
if not fpath.is_file():
continue
fpath.write_text(fpath.read_text().replace('foo', 'bar'))

Just add the quotes to tell xonsh that {} and + are the arguments:
find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' '{}' '+'
The complete example of testing your command in xonsh:
echo 'foo foo' > some_baz_file
find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' '{}' '+'
cat some_baz_file
# bar bar
Another way is to use the xonsh macro command and just run bash line as is:
bash -c! find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +
To simplify this approach there is xontrib-sh extension that allows just add the ! sign before the command:
! find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +

Related

Using find recursively and only returning the matching file names

I am trying to find all instances of a string within files - I am using find and it works great, however, it returns not only the file but every instance of that string within the file which results in a huge long list whereas I really only want the file name.
I am using:
find . -name '*.php' -exec grep -i 'MATCH' {} \; -print
This will show me every instance of MATCH and then the file name then the next batch and the filename so something like:
MATCH
MATCH
MATCH
./filename
MATCH
MATCH
MATCH
./filename2
I tried changing GREP to:
find . -name '*.php' -exec grep -H -i 'MATCH' {} \; -print
and this then gave me:
./filename: MATCH
./filename: MATCH
./filename: MATCH
./filename2: MATCH
./filename2: MATCH
./filename2: MATCH
however this still results in the same number of lines being shown all be it slightly differently laid out.
I tried changing GREP to:
find . -name '*.php' -exec grep -l -i 'MATCH' {} \; -print
and this then gave me:
./filename
./filename
./filename
./filename2
./filename2
./filename2
Ideally I would like something like:
./filename
./filename2
which only lists each of the file which match once regardless of how many times it appears in each file - can this be done?
Use the features provided by grep:
-l, --files-with-matches print only names of FILEs containing matches
-R, -r, --recursive equivalent to --directories=recurse
--include=FILE_PATTERN search only files that match FILE_PATTERN
-i, --ignore-case ignore case distinctions
I.e.
grep -ril --include="*.php" 'MATCH' .
It's not the grep that's the problem, you're telling find to print every file name:
find . -name '*.php' -exec grep -l -i 'MATCH' {} \; -print
Note the -print at the end. Just remove that:
find . -name '*.php' -exec grep -l -i 'MATCH' {} \;
Look:
$ echo 'foo' > file1
$ echo 'foo' > file2
$ find . -name 'file*' -exec grep -l -i foo {} \; -print
./file1
./file1
./file2
./file2
$ find . -name 'file*' -exec grep -l -i foo {} \;
./file1
./file2

find + grep to match exact keywords in a file

My script is not matching exact words only. Example: 12312312Alachua21321 or Alachuas would match for Alachua.
KEYWORDS=("Alachua" "Gainesville" "Hawthorne")
IFS=$'\n'
find . -size +1c -type f ! -exec grep -qF "${KEYWORDS[*]}" {} \; -exec truncate -s 0 {} \;
If you want grep to match exact words, use grep -w.
You may also want to read the grep manual by running man grep.

grep returns No such file or directory in Script but works manually

What i wanted was to make a little Script that makes a grep over files that successfully passed another grep.
Here is that i wrote:
#!/bin/bash -f
if (( $# != 3 ))
then
echo "Usage: $0 <directory> <grep1> <grep2>"
exit 1
fi
dir=$1
grep1=$2
grep2=$3
files=`grep -Ril "$grep1" $dir`
for file in $files; do
grep -iH "$grep2" $file
done
so this works, in case the directory is a specific one.
But It doesnt work in the way i wanted to use it:
./grepIfSucceededGrep.sh /mnt/logs/\*/20130929/\* test1 test2
grep: /mnt/logs/*/20130929/*: No such file or directory
But when i enter the command manually it works perfectly. Must have to do something with the asterisk.
grep -Ril "segment1" /mnt/logs/*/20130929/*
/mnt/logs/1111/20130929/000033.00.log
/mnt/logs/1112/20130929/000033.00.log
/mnt/logs/1113/20130929/154852.00.log
/mnt/logs/1114/20130929/171227.00.log
Why?! :( just searched for 2 hours to get a workarround but had no fix for that issue.
Do you really need a script to achieve what you need? You can say:
find /path -type f -exec sh -c "grep -iq string1 {} && grep -iq string2 {} && echo {}" \;
This would list the files in /path and subdirectories that contain both string1 and string2.
If you need to put it into a script so that you could pass parameters, say:
find $1 -type f -exec sh -c "grep -iq $2 {} && grep -iq $3 {} && echo {}" \;
within a file, say myfind.sh that could be invoked by saying sh myfind.sh /my/path/to/search firststring secondstring.

find grep exclude some file names for dos2unix

so far I have gotten this far:
prompt$ find path/to/project -type f | grep -v '*.ori|*.pte|*.uh|*.mna' | xargs dos2unix 2> log.txt
However, the files with extensions .ori, .pte, .uh and .mna still show up.
It is better to leave the excluding to find, see Birei's answer.
The problem with your grep pattern is that you have specified it as a shell glob. By default grep expects basic regular expressions (BRE) as its first argument. So if you replace your grep pattern with: .*\.\(ori\|pte\|uh\|mna\)$ it should work. Or if you would rather use extended regular expressions (ERE), you can enable them with -E. Then you can express the same exclusion like this: .*\.(ori|pte|uh|mna)$.
Full command-line:
find . -type f | grep -vE '.*\.(ori|pte|uh|mna)$'
One way:
find path/to/project *.* -type f ! \( -name '*.ori' -o -name '*.pte' -o -name '*.uh' -o -name '*.mna' \)
| xargs dos2unix 2> log.txt

grep multiple extension current and subfolders

I'm trying to grep multiple extensions within the current and all sub-folders.
grep -i -r -n 'hello' somepath/*.{php,html}
This is only grepping the current folder but not sub-folders.
What would be a good way of doing this?
Using only grep:
grep -irn --include='*.php' --include='*.html' 'hello' somepath/
One of these:
find '(' -name '*.php' -o -name '*.html' ')' -exec grep -i -n hello {} +
find '(' -name '*.php' -o -name '*.html' ')' -print0 | xargs -0 grep -i -n hello
I was looking the same and when decided to do a bash script I started with vim codesearch and surprise I already did this before!
#!/bin/bash
context="$3"
#ln = line number mt = match mc = file
export GREP_COLORS="sl=32:mc=00;33:ms=05;40;31:ln="
if [[ "$context" == "" ]]; then context=5; fi
grep --color=always -n -a -R -i -C"$context" --exclude='*.mp*'\
--exclude='*.avi'\
--exclude='*.flv'\
--exclude='*.png'\
--exclude='*.gif'\
--exclude='*.jpg'\
--exclude='*.wav'\
--exclude='*.rar'\
--exclude='*.zip'\
--exclude='*.gz'\
--exclude='*.sql' "$2" "$1" | less -R
paste this code into in a file named codesearch and set the chmod to 700 or 770
I guess this could be better here for the next time that I forgot
this script will show with colors the matches and the context around
./codesearch '/full/path' 'string to search'
and optional defining the number of context line around default 5
./codesearch '/full/path' 'string to search' 3
I edited the code and added some eye candy
example ./codesearch ./ 'eval' 2
Looks like this when you have enabled "allow blinking text" in terminal

Resources