Andy Holmes Andy Holmes - 4 months ago 9
Bash Question

Why is this script creating a duplicate file and adding `-e` to the end?

I will firstly say that I'm not sure if this should be here or SuperUser, so let me know if I need to remove.

I'm making a function inside

~./bash_profile
to help me when I'm starting up new Laravel projects. Here's the script:

function bootlaravel {
PROJECT=$1
echo "Magically creating a new Laravel project: $PROJECT..."
cd /Applications/MAMP/htdocs
laravel new $PROJECT
cd $PROJECT
git init
git add .
git commit -m "Install Laravel"
sed -i -e "s/DB_DATABASE=homestead/DB_DATABASE=$PROJECT/g" .env
sed -i -e "s/DB_USERNAME=homestead/DB_USERNAME=root/g" .env
sed -i -e "s/DB_PASSWORD=secret/DB_PASSWORD=/g" .env
/Applications/MAMP/Library/bin/mysql --host=localhost -uroot -proot -e "create database $PROJECT DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci"
open "http://$PROJECT.dev"
pstorm .
echo "Project: $PROJECT has been created. Have fun!"
}


Usage is something like
bootlaravel LaravelProjectName


Where the
sed
commands are being used to modify the
.env
file, it's actually creating a
.env-e
file as a sibling to the existing
.env
file. I can't actually understand why it's doing this. It changes the words as required fine though.

Any help is appreciated

Answer

I generally advise against using sed -i at all, especially when trying things out or moving scripts between platforms. The BSD sed (on OS X for example) requires an argument to -i, and this is probably also why you get an -e suffix on your file (and definitely why -ie gives you an e suffix, even with GNU sed).

Your three invocations of sed may be combined:

sed -e 's/DB_DATABASE=homestead/DB_DATABASE='"$PROJECT"'/g' \
    -e 's/DB_USERNAME=homestead/DB_USERNAME=root/g' \
    -e 's/DB_PASSWORD=secret/DB_PASSWORD=/g' .env >.env-new &&
mv -f .env-new .env

In my mind, using a temporary file is a small price to pay for portability and robustness.

Additionally, quote all user-supplied strings. In your case, $PROJECT and $1, you have a nice SQL injection possibility there otherwise, for example.

If I got the SQL quoting right, this would be the modified shell function:

function bootlaravel {
    local project="$1"

    echo "Magically creating a new Laravel project: $project..."

    cd /Applications/MAMP/htdocs || return 1    
    laravel new "$project"
    cd "$project" || return 1

    git init
    git add .
    git commit -m "Install Laravel"

    sed -e 's/DB_DATABASE=homestead/DB_DATABASE='"$project"'/g' \
        -e 's/DB_USERNAME=homestead/DB_USERNAME=root/g' \
        -e 's/DB_PASSWORD=secret/DB_PASSWORD=/g' .env >.env-new &&
    mv -f .env-new .env

    /Applications/MAMP/Library/bin/mysql \
        --host=localhost -uroot -proot <<SQL_END
CREATE DATABASE '$project'
DEFAULT CHARACTER SET utf8
DEFAULT COLLATE utf8_general_ci
SQL_END

    open "http://$project.dev"
    pstorm .

    echo "Project: $project has been created. Have fun!"
}

This also uses a lowercase $project (uppercase generally used for environment variables), and will return a non-zero status if any of the cd fails. I also declare project as a local variable in the function as to not pollute the calling shell with new shell variables. The empty lines etc. are mostly for readability.

Comments