0

Shell Scripting sed Errors:

Cannot view /home/xx/htdocs/*/modules/forms/int.php

/bin/rm: cannot remove `/home/xx/htdocs/tmp.26758': No such file or directory

I am getting an error in my shell script. I am not sure if this for loop will work, it is intended to climb a large directory tree of PHP files and prepend a functions in every int.php file with a little validation. Don't ask me why this wasn't centralized/OO but it wasn't. I copied the script as best I could from here: http://www.cyberciti.biz/faq/unix-linux-replace-string-words-in-many-files/

#!/bin/bash
OLD="public function displayFunction(\$int)\n{"
NEW="public function displayFunction(\$int)\n{if(empty(\$int) || !is_numeric(\$int)){return '<p>Invalid ID.</p>';}"
DPATH="/home/xx/htdocs/*/modules/forms/int.php"
BPATH="/home/xx/htdocs/BAK/"
TFILE="/home/xx/htdocs/tmp.$$"
[ ! -d $BPATH ] && mkdir -p $BPATH || :
for f in $DPATH
do 
 if [ -f $f -a -r $f ]; then
   /bin/cp -f $f $BPATH
   sed "s/$OLD/$NEW/g" "$f" > $TFILE && mv $TFILE "$f"
 else
  echo "Error: Cannot view  ${f}"
 fi
done
/bin/rm $TFILE

Do wildcards like this even work? Can I check in every subdirectory across a tree like this? Do I need to precode an array and loop over that? How would I go about doing this?

Also is, the $ in the PHP code breaking the script at all?

I am terribly confused.

XaxD
  • 1,429
  • 14
  • 27
  • Are you sure you don't want `init.php` and not `int.php` as you have written? Else, either turn on the shell debugging feature with `set -vx` (or `set -x`), to see what values are being used for each variable OR add an `echo f=$f` right after the `for f in $DPATH`. I would expect `*` to expand if there are files `module/forms/int.php` under the subdirs of `.../htdocs`. Good luck. – shellter Sep 24 '14 at 03:47

1 Answers1

1

Problems in your code

  • You cannot use sed to replace multiple lines this way.
  • you are using / in OLD which is used in a s/// sed command. This won't work
  • [ ! -d $BPATH ] && mkdir -p $BPATH || : is horrible. use mkdir -p "$bpath" 2>/dev/null
  • Yes, wildcards like this will work but only because your string has no spaces
  • Doube-quote your variables, or your code will be very dangerous
  • Single quote your strings or you won't understand what you are escaping
  • Do not use capital variable names, you could accidentally replace a bash inner variable
  • do not rm a file that does not exist
  • Your backups will be overwritten as all files are named int.php

Assuming you are using GNU sed, I'm not used to other sed flavors. If you are not using GNU sed, replacing the \n with a newline (inside the string) should work.

Fixed Code

#!/usr/bin/env bash
old='public function displayFunction(\$int)\n{'
old=${old//,/\\,} # escaping eventual commas
# the \$ is for escaping the sed-special meaning of $ in the search field
new='public function displayFunction($int)\n{if(empty($int) || !is_numeric($int)){return "<p>Invalid ID.</p>";}\n'
new=${new//,/\\,} # escaping eventual commas
dpath='/home/xx/htdocs/*/modules/forms/int.php'
for f in $dpath; do 
    [ -r "$f" ]; then
        sed -i.bak ':a;N;$!ba;'"s,$old,$new,g" "$f"
    else
       echo "Error: Cannot view  $f" >&2
    fi
done

Links

Community
  • 1
  • 1
Camusensei
  • 1,475
  • 12
  • 20
  • Thanks for the very thorough answer. I ended up writing a php script that implemented a reflector class, but thank you kindly stranger :) – XaxD May 05 '15 at 02:39