Recursive sed shell function doesn't do anything - bash

Referring to other Q/As on SO, I added the following to my .bashrc:
function findandreplace {
find . -type f -name "$1" -not -path "*/.git/*" -print0 | xargs -0 sed -i 's/$2/$3/g'
}
Oddly, it doesn't do anything. When I change it to:
function findandreplace {
echo "find . -type f -name \"$1\" -not -path \"*/.git/*\" -print0 | xargs -0 sed -i 's/$2/$3/g'"
}
I get
$ findandreplace "*.cpp" "A.cpp" "B.cpp"
find . -type f -name "*.cpp" -not -path "*/.git/*" -print0 | xargs -0 sed -i 's/A.cpp/B.cpp/g'
as expected. Copy-pasting that command then performs the expected operation.
What's wrong with my initial function?

The 's/$2/$3/g' part doesn't make sense; single-quotes prevent parameter-expansion, so your actual sed script is s/$2/$3/g (dollar signs and all) rather than s/A.cpp/B.cpp/g. Since $ in a regex means "end-of-string" (or sometimes "end-of-line"), $2 will never match anything.
To fix this, you can use double-quotes instead:
function findandreplace {
find . -type f -name "$1" -not -path "*/.git/*" -print0 \
| xargs -0 sed -i "s/$2/$3/g"
}
with the caveat that this will misbehave if $2 or $3 contains slashes. (Edited to add:) To fix that, you can ask Bash to replace / with \/ in those parameters, though it's pretty ugly:
function findandreplace {
find . -type f -name "$1" -not -path "*/.git/*" -print0 \
| xargs -0 sed -i "s/${2//\//\/}/${3//\//\/}/g"
}

Because of use of single quotes:
sed -i 's/$2/$3/g'
won't work since shell won't expand these variables. Use sed like this:
sed -i "s/$2/$3/g"

Related

KSH88 variable inside script

I'm having trouble with KSH88
script="find . ! \( "$result" \) -mtime "$older" -xdev -type f -size +"$minsize"M -exec ls -lh {} \; | head -100 | awk '{print \$8}' | sort -rn"
files_to_delete=`$script`
When I Echo my files_to_delete variable I get :
find . ! \( -name '*.jar' -o -name '*.zip' -o -name '*.rar' -o -name '*.log' -o -name '*.xml' \) -mtime 10 -xdev -type f -size +100M -exec ls -lh {} \; | head -100 | awk '{print $8}' | sort -rn
which is what I want, when I execute it on the command line it works, but when I execute it in my KSH I get
find: bad option \(
find: [-H | -L] path-list predicate-list
Put "eval " in front of the "$script", so it becomes
files_to_delete=`eval $script`
This forces the shell to evaluate the command string.
If your shell supports it, it woudl be better to use files_to_delete=$(eval $script). The ` version is easier to miss when scanning the script quickly, and much harder to nest (commands within commands).

Bash - file path can not be read for LAME encoder

How do I properly escape the path to come out of find to a new command argument?
#!/bin/bash
for f in $(find . -type f -name '*.flac')
do
if flac -cd "$f" | lame -bh 320 - "${f%.*}".mp3; then
rm -f "$f"
echo "removed $f"
fi
done
returns
lame: excess arg Island of the Gods - 3.mp3
Using a Bash for loop is not ideal for the results of find or ls. There are other ways to do it.
You may want to use -print0 and xargs to avoid word splitting issues.
$ find [path] -type f -name *.flac -print0 | xargs -0 [command line {xargs puts in fn}]
Or use -exec primary in find:
$ find [path] -type f -name *.flac -exec [process {find puts in fn}] \;
Alternative, you can use a while loop:
find [path] -type f -name *.flac | while IFS= read -r fn; do # fn not quoted here...
echo "$fn" # QUOTE fn here!
# body of your loop
done

How to combine these 2 commands into single line command

I have 2 useful bash command below, but i want to combine it together.
Is it possible to do ?
find "$1" -type f -print0 | xargs -0 sha1sum -b
find "$1" -type f ! -iname '*thumbs.db*' -print0 | xargs -0 stat -c "%y %s %n"
If you want to write it in one line, you can just use "&" to combine the commands. Maybe this is what you meant:
find "$1" -type f -print0 | xargs -0 sha1sum -b & find "$1" -type f ! -iname '*thumbs.db*' -print0 | xargs -0 stat -c "%y %s %n"

Need help omitting folders in find command

I have this line in a script I'm writing
find / \( -perm -4000 -o -perm -2000 \) -type f -exec file {} \; | grep -v ELF | cut -d":" -f1 >> $OUTPUT
It does the work, BUT I always get these messages I want to omit
find: `/proc/29527/task/29527/fd/5': No such file or directory
find: `/proc/29527/task/29527/fdinfo/5': No such file or directory
find: `/proc/29527/fd/5': No such file or directory
find: `/proc/29527/fdinfo/5': No such file or directory
How can I omit the /proc directory?
I believe this should work:
find / -path /proc -prune -o \( -perm -4000 -o -perm -2000 \) -type f ...
^^^^^^^^^^^^^^^^^^^^^ Add this to your command line
What if you redirect STDERR to /dev/null. That way, you don't see the unwanted error/warning in your TTY (STDOUT) like
{ find / \( -perm -4000 -o -perm -2000 \) -type f -exec file {} \; | grep -v ELF | cut -d":" -f1 >> $OUTPUT; } 2>/dev/null
The following prunes the proc directory:
find / -name /proc -prune -o \
\( -perm -4000 -o -perm -2000 \) -type f \
-exec file {} \; | grep -v ELF | cut -d":" -f1 >> $OUTPUT

sed pattern ending with a specific character or the end of line

I use grep v2.5.1 and I want to colorize the filename within the grep output.
I could use another grep command with pattern /[^/:]*\(:\|$\):
grep --color=always something */* | grep --color '/[^/:]*\(:\|$\)'
and this same pattern also works to list files:
grep --color=always something */* -l | grep --color '/[^/:]*\(:\|$\)'
But I would prefer a sed command, and I do not know how to translate \(:\|$\) in sed :-(
For instance:
echo 'dir/file: xxxx' | sed 's|/\([^/:]*\)(:|$)|/\o033[1;35m\1\o033[0m\2|'
FYI, my complete function in ~/.bashrc
gg() {
find . -name .svn -prune -o -type f '(' -name '*.java' -o -name '*.h' -o -name '*.cpp' -o -name 'Make*' -o -name '*.sh' ')' -print0 |
xargs -0 grep --color=always "$#" |
sed 's|/\([^/:]*\)(:|$)|/\o033[1;35m\1\o033[0m\2|'
}
After trying some other possibilities I finally found:
grep pattern is same as sed pattern for this purpose
And my complete function is:
gg ()
{
find . -path '*/.svn' -prune -o -type f '(' -name '*.java' -o -name '*.h' -o -name '*.hpp' -o -name '*.hxx' -o -name '*.cpp' -o -name '*.cxx' -o -name '*.c' -o -name '[Mm]akefi*[^~]' -o -name '*.sh' -o -iname '*.xml' ')' -exec grep --color=always "$#" '{}' '+' |
sed -u 's_\(/\|^\)\([^/:]*\)\(:\|$\)_\1\o033[1;37m\2\o033[0m\3_'
}
I am still open for any comments, suggestions, improvements, contributions...
cheers ;-)

Resources