bash scripting challenge - bash

I need to write a bash script that will iterate through the contents of a directory (including subdirectories) and perform the following replacements:
replace 'foo' in any file names with 'bar'
replace 'foo' in the contents of any files with 'bar'
So far all I've got is
find . -name '*' -exec {} \;
:-)

With RH rename:
find -f \( -exec sed -i s/foo/bar/g \; , -name \*foo\* -exec rename foo bar {} \; \)

find "$#" -depth -exec sed -i -e s/foo/bar/g {} \; , -name '*foo*' -print0 |
while read -d '' file; do
base=$(basename "$file")
mv "$file" "$(dirname "$file")/${base//foo/bar}"
done

UPDATED: 1632 EST
Now handles whitespace but 'while read item' never terminates. Better,
but still not right. Will keep
working on this.
aj#mmdev0:~/foo_to_bar$ cat script.sh
#!/bin/bash
dirty=true
while ${dirty}
do
find ./ -name "*" |sed -s 's/ /\ /g'|while read item
do
if [[ ${item} == "./script.sh" ]]
then
continue
fi
echo "working on: ${item}"
if [[ ${item} == *foo* ]]
then
rename 's/foo/bar/' "${item}"
dirty=true
break
fi
if [[ ! -d ${item} ]]
then
cat "${item}" |sed -e 's/foo/bar/g' > "${item}".sed; mv "${item}".sed "${item}"
fi
dirty=false
done
done

#!/bin/bash
function RecurseDirs
{
oldIFS=$IFS
IFS=$'\n'
for f in *
do
if [[ -f "${f}" ]]; then
newf=`echo "${f}" | sed -e 's/foo/bar/g'`
sed -e 's/foo/bar/g' < "${f}" > "${newf}"
fi
if [[ -d "${f}" && "${f}" != '.' && "${f}" != '..' && ! -L "${f}" ]]; then
cd "${f}"
RecurseDirs .
cd ..
fi
done
IFS=$oldIFS
}
RecurseDirs .

bash 4.0
#!/bin/bash
shopt -s globstar
path="/path"
cd $path
for file in **
do
if [ -d "$file" ] && [[ "$file" =~ ".*foo.*" ]];then
echo mv "$file" "${file//foo/bar}"
elif [ -f "$file" ];then
while read -r line
do
case "$line" in
*foo*) line="${line//foo/bar}";;
esac
echo "$line"
done < "$file" > temp
echo mv temp "$file"
fi
done
remove the 'echo' to commit changes

for f in `tree -fi | grep foo`; do sed -i -e 's/foo/bar/g' $f ; done

Yet another find-exec solution:
find . -type f -exec bash -c '
path="{}";
dirName="${path%/*}";
baseName="${path##*/}";
nbaseName="${baseName/foo/bar}";
#nbaseName="${baseName//foo/bar}";
# cf. http://www.bash-hackers.org/wiki/doku.php?id=howto:edit-ed
ed -s "${path}" <<< $'H\ng/foo/s/foo/bar/g\nwq';
#sed -i "" -e 's/foo/bar/g' "${path}"; # alternative for large files
exec mv -iv "{}" "${dirName}/${nbaseName}"
' \;

correction to find-exec approach by gregb (adding quotes):
# compare
bash -c '
echo $'a\nb\nc'
'
bash -c '
echo $'"'a\nb\nc'"'
'
# therefore we need
find . -type f -exec bash -c '
...
ed -s "${path}" <<< $'"'H\ng/foo/s/foo/bar/g\nwq'"';
...
' \;

Related

Send an email if grep has results

I'm trying to do a simple script that if the "grep" comes with results, send the email with the results. But I want also to send an email if there is a case when there are no rejects
#! /bin/bash
FILE=$(find . /opt/FIXLOGS/l51prdsrv* -iname "TRADX_*.log" -type f -exec grep -F 103=16 {} /dev/null \; )>> Rejects.txt
if [ "$FILE" == true ]
then
mailx -s "Logs $(date +%d-%m-%y)" "email" < Rejects.txt
rm -f Rejects.txt
elif [ "$FILE" == null ]
then
echo "No Rejects" >> Rejects.txt
mailx -s "Logs $(date +%d-%m-%y)" "email" < Rejects.txt
rm -f Rejects.txt
fi
In bash, everything is pretty much just a string. null is a string, not a null reference like in other languages. true is also a string (unless it's the command true, but that's not the case in your comparison).
If you want to test that a file exists, you'd use [[ -f "$FILE" ]]. However, the file is going to exist in your case even if grep matches nothing because bash automatically creates the file when you set it as the destination for your output. What you really need is -s which tests if the file exists and has size greater than 0.
#!/bin/bash
find . /opt/FIXLOGS/l51prdsrv* -iname "TRADX_*.log" -type f -exec grep -F 103=16 {} /dev/null \; >> Rejects.txt
if [[ -s Rejects.txt ]] ; then
: # grep wrote stuff into the file
else
: # file is empty so grep found nothing
fi

Finding a particular string at a specific line number, anywhere in a directory

I am trying to find instances of the word package in line 171 of any file in a certain directory. What is the best way to do this?
You can do, from the directory you want to check, recursively:
find . -type f -exec bash -c '[[ $(sed -n '171p' "$1") =~ package ]] && echo "$1"' _ {} +
this will show you the filenames that contain package in their 171-th line.
Non-recursively:
find . -maxdepth 1 -type f -exec bash -c '[[ $(sed -n '171p' "$1") =~ package ]] && echo "$1"' _ {} +
Example:
I am looking for bar:
$ cat foo
foo
bar
$ find . -type f -exec bash -c '[[ $(sed -n '2p' "$1") =~ bar ]] && echo "$1"' _ {} +
./foo
#This will search second line of all the files and grep over them.
#Pass search pattern as argument.
pattern=$1
for file in *
do
cat $file |sed -n '2p' |grep $pattern
done

iterate over lines in file then find in directory

I am having trouble looping and searching. It seems that the loop is not waiting for the find to finish. What am I doing wrong?
I made a loop the reads a file line by line. I then want to use that "name" to search a directory looking to see if a folder has that name. If it exists copy it to a drive.
#!/bin/bash
DIRFIND="$2"
DIRCOPY="$3"
if [ -d $DIRFIND ]; then
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "$line"
FILE=`find "$DIRFIND" -type d -name "$line"`
if [ -n "$FILE" ]; then
echo "Found $FILE"
cp -a "$FILE" "$DIRCOPY"
else
echo "$line not found."
fi
done < "$1"
else
echo "No such file or directory"
fi
Have you tried xargs...
Proposed Solution
cat filenamelist | xargs -n1 -I {} find . -type d -name {} -print | xargs -n1 -I {} mv {} .
what the above does is pipe a list of filenames into find (one at a time), when found find prints the name and passes to xarg which moves the file...
Expansion
file = yogo
yogo -> | xargs -n1 -I yogo find . -type d -name yogo -print | xargs -n1 -I {} mv ./<path>/yogo .
I hope the above helps, note that xargs has the advantage that you do not run out of command line buffer.

Count the deleted files Shell script

Can you tell me how can you count the files with the extension ".txt" you delete from a folder? Shell script in Unix
Thank you for your answer :)
I tried to delete them this way :
deleted=0
while read line
do
if test -d "$line"
then
for i in "$line"/*
do
if test -f "$i"
then
deleted=`ls -l $line |grep "*.o" | wc -l`
echo "From: " $line " I deleted : " $deleted
find . -type f -name "*.o" -exec rm -f {} \;
else
echo "Not file " $i
fi
done
else
echo "NOT a directory!"
fi
done
Try doing this :
LANG=C rm -v *.txt | grep -c "^removed "
An answer - though not necessarily the right one:
files=*.txt
ls -1 "$files" | wc -l
rm "$files"
Ruth

Simplest Bash code to find what files from a defined list don't exist in a directory?

This is what I came up with. It works perfectly -- I'm just curious if there's a smaller/crunchier way to do it. (wondering if possible without a loop)
files='file1|file2|file3|file4|file5'
path='/my/path'
found=$(find "$path" -regextype posix-extended -type f -regex ".*\/($files)")
for file in $(echo "$files" | tr '|', ' ')
do
if [[ ! "$found" =~ "$file" ]]
then
echo "$file"
fi
done
You can do this without invoking any external tools:
IFS="|"
for file in $files
do
[ -f "$file" ] || printf "%s\n" "$file"
done
Your code will break if you have file names with whitespace. This is how I would do it, which is a bit more concise.
echo "$files" | tr '|' '\n' | while read file; do
[ -e "$file" ] || echo "$file"
done
You can probably play around with xargs if you want to get rid of the loop all together.
$ eval "ls $path/{${files//|/,}} 2>&1 1>/dev/null | awk '{print \$4}' | tr -d :"
Or use awk
$ echo -n $files | awk -v path=$path -v RS='|' '{printf("! [[ -e %s ]] && echo %s\n", path"/"$0, path"/"$0) | "bash"}'
without whitespace in filenames:
files=(mbox todo watt zoff xorf)
for f in ${files[#]}; do test -f $f || echo $f ; done

Resources