Avoid $var to be interpreted as a variable inside inverted commas - bash

I have tried in many ways but couldn't get it the right way.
fun="
mkcdo ()
{
mkdir -p -- \"'$1'\" && cd -P -- \"'$1'\"
}"
echo "$fun" >> ~/.bashrc
What I want is to append this in .bashrc
mkcd ()
{
mkdir -p -- "$1" && cd -P -- "$1"
}
Could that be done? Is there a way in bash like there is in python: r'whatever\you\$write' so that it is completely ignored as simple text?

Using a variable to store a bash function code sounds much of anti-pattern. For multi-line formatted strings, I would recommend using here-doc and quote them to avoid expanding the variable,
cat >> ~/.bashrc << 'EOF'
mkcd () {
mkdir -p -- "$1" && cd -P -- "$1"
}
EOF
Further reading - Bash - Here documents

Alternative1: just use single quotes.
fun='
mkcdo ()
{
mkdir -p -- "$1" && cd -P -- "$1"
}'
echo "$fun" >> ~/.bashrc
Alternative2: escape the $ sign.
fun="
mkcdo ()
{
mkdir -p -- \"\$1\" && cd -P -- \"\$1\"
}"
echo "$fun" >> ~/.bashrc

Related

How to create files in the shell where variables are not replaced

I need to create a file for subsequent nohup execution, the file contains some variables, I don't want the variables to be replaced when I generate through the file.
When I executed the code with the following code, all the variables were replaced in the generated script, which was not what I expected.
#!/bin/bash
gen_script() {
filepath=$1
if [ ! -f "$filepath" ]; then
cat >${filepath} <<EOF
#!/bin/bash
# Code generated by main.sh; DO NOT EDIT.
test(){
ip=$1
port=$2
restorefile=$3
redis-cli -h $ip -p $port --pipe < $restorefile
}
test "$#"
EOF
fi
}
main(){
gen_script exec.sh
nohup bash exec.sh $1 $2 > nohup.out 2>&1 &
}
main "$#"
How can I change my code please? I really appreciate any help with this.
To disable expansions in here document, quote the delimieter:
cat <<'EOF'
... $not_expanded
EOF
Instead, let bash serialize the function.
#!/bin/bash
work() {
ip=$1
port=$2
restorefile=$3
redis-cli -h $ip -p $port --pipe < $restorefile
}
gen_script() {
filepath=$1
if [ ! -f "$filepath" ]; then
cat >${filepath} <<EOF
#!/bin/bash
# Code generated by main.sh; DO NOT EDIT.
$(declare -f work)
work "\$#"
EOF
fi
}
main() {
gen_script exec.sh
nohup bash exec.sh "$1" "$2" > nohup.out 2>&1 &
}
main "$#"
Check your script with shellcheck. Do not define a function named test, there is already a super standard command test which is an alias for command [.

Best way for conditionally (and verbosely) run a command in bash

I have written a shell script (bash) which runs some commands. It has an option to not to run the commands but to echo them to the screen. By default, the output of these commands is redirected to /dev/null but there is another option to show the output on the screen.
I use a function to check for the value of these variables and run the commands or simulate them:
runmaybe() {
if [[ true = $dry_run ]]; then
echo "Simulating '$#'"
else
if [[ true = $verbose ]]; then
$#
else
$# > /dev/null
fi
fi
}
The function is working but I had some issues with complex commands such as:
runmaybe eval svn cp $url $root/tags/$ntag -m \"Tagging revision with $ntag\"
I had to add the eval to prevent wordsplitting so svn gets the right value for the -m option.
I have some other complex commands in that script such as:
runmaybe vzctl exec 1 "( cd /var/wwww/vhosts/myhost ; php cron.php )"
runmaybe ssh -t user#$host "vzctl exec $vmid \"( /usr/local/bin/myscript )\"" 2>/dev/null
runmaybe rsync --delete --exclude=\"**/.svn/\" --exclude=\"**/.git/\" --include=*.exe --numeric-ids -a $vOpt -H $LOCAL_VM$dir $host:$REMOTE_VM$dir
Although the script is working right now, I wonder if there is a better way of doing this task.
The problem is in unquoted expansion of $#. As a rule of a thumb, if you see $, you should put it inside ". Unquoted expansions undergo word splitting and filename expansions - to prevent them, quote the expansion.
I had to add the eval
eval is evil. Do not use it.
runmaybe() {
if [[ true = $dry_run ]]; then
echo "Simulating '$*'"
# or better quote with printf in some corner cases
printf "Simulating:"
printf " %q" "$#"
printf "\n"
elif [[ true = $verbose ]]; then
"$#"
else
"$#" > /dev/null
fi
}
runmaybe svn cp "$url" "$root/tags/$ntag" -m "Tagging revision with $ntag"
runmaybe rsync --delete --exclude="**/.svn/" --exclude="**/.git/" --include="*.exe" --numeric-ids -a "$vOpt" -H "$LOCAL_VM$dir" "$host:$REMOTE_VM$dir"

How to escape double quotes in bash command

I have the following command, which I am trying to append to my ~/.bash_profile:
echo 'alias N="cd $(pwd) && source ./bin/activate && cd new""
I want it to return echo:
alias N="cd /Users/david/Desktop/django2 && source ./bin/activate && cd new"
What would be the correct way to escape the pwd or quotes?
This should work for you:
echo "alias N='cd $(pwd) && source ./bin/activate && cd new'"
However recommend you to use function instead of alias.
echo "N() { cd '$PWD' && source ./bin/activate && cd new; }"
Note use of $PWD instead of command pwd
To echo a literal " inside of a double-quoted string, use the escape character \:
echo "alias N=\"cd $(pwd) && source ./bin/activate && cd new\""
Outputs on my box:
alias N="cd /home/bishop/ && source ./bin/activate && cd new"
However, your use case suggests to me a function:
go() {
local where
where=${1:-${PWD}}
cd "${where}" && source ./bin/activate && cd new
}
Avoid excessive escaping with a here document:
cat >> ~/.bash_profile << EOF
alias N="cd $(pwd) && source ./bin/activate && cd new"
EOF

get the script path with whitespaces in Bash

I'm running it under MacOS El Capitan 10.10.6
among all commands to get my current dir (path I'm running my script from) only this works for me:
FILES="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
But it's not going to work if the folder has whitespace in it's name (aka: "folder name")
How to fix this?
Thank you! )
Update: added a script:
#!/bin/bash
function check ()
{
oldsize=`wc -c <"$1"`
sleep 1
newsize=`wc -c <"$1"`
while [ "$oldsize" -lt "$newsize" ]
do
echo "Not yet..."
oldsize=`wc -c <"$1"`
sleep 1
newsize=`wc -c <"$1"`
done
if [ "$oldsize" -eq "$newsize" ]
then
echo "The file has been copied completely."
fi
}
FILES="$(dirname "${BASH_SOURCE[0]}")/*"
function main
{
for f in $FILES
do
if [[ "$f" =~ \.mkv$ ]];
then
#/////////////////////////////////////////////////////////////////////
check "$f"
(( count = count + 1 ))
g="${f/mkv/avi}"
#LOG_FILE="${g/avi/log}"
#exec > >(tee -a "${LOG_FILE}" )
#exec 2> >(tee -a "${LOG_FILE}" >&2)
now="$(date)"
printf "Current date and time %s\n" "$now"
echo "Processing $f file..."
#avconv -i "${f}" -map 0:0 -map 0:1 -codec copy -sn "${g}"
avconv -i "$f" -map 0 -codec copy "$g"
if [ $? -eq 0 ]; then
echo OK
rm "$f"
else
echo FAIL
rm "$g"
#rm "$LOG_FILE"
return
fi
fi
#/////////////////////////////////////////////////////////////////////
done
}
############
count=0
############
main
if (($count > 0)); then
open "$(dirname "${BASH_SOURCE[0]}")"
fi
exit
I am using Mac OS X 10.11.6, and I have a directory $HOME/tmp. From there, I executed:
$ cd $HOME/tmp
$ pwd
/Users/jleffler/tmp
$ mkdir -p "Spaced Out Directory "/bin
$ export PATH="$PATH:$PWD/$_"
$ cat <<'EOF' > Spaced\ Out\ \ Directory\ \ \ /bin/gorblinsky.sh
> #!/bin/bash
>
> echo "PWD=$PWD"
> DIR="$(dirname "${BASH_SOURCE[0]}")"
> echo "DIR=$DIR"
> cd "$DIR"
> pwd
> echo "PWD=$PWD"
> EOF
$ chmod +x Spaced\ Out\ \ Directory\ \ \ /bin/gorblinsky.sh
$ gorblinsky.sh
PWD=/Users/jleffler/tmp
DIR=/Users/jleffler/tmp/Spaced Out Directory /bin
/Users/jleffler/tmp/Spaced Out Directory /bin
PWD=/Users/jleffler/tmp/Spaced Out Directory /bin
$
This shows that the command $(dirname "${BASH_SOURCE[0]}") can determine the name of the directory where the source for the command is stored.
If the script was going to use the variable $DIR to specify file names, you'd need to be careful (very careful) to ensure it is always properly quoted.
For example:
cp "$DIR/gorblinksky.h" "$HOME/tmp/cobbled together name"
Modern style is to always (double) quote all variable references, even when there's nothing in them that needs protecting (see shellcheck.net for example — and Google Shell Style Guide). I'm old-school enough not to put quotes around names that can't contain spaces or metacharacters, but I guess that is just old-fashioned. For example, I shell-checked a script for playing with RCS version numbers, and it doesn't quote variables containing dotted strings of digits (9.19.2.24 — could be an IBM IPv4 address too) and I was told off for not quoting them, though the file names were already protected with quotes.

How do i populate this variable in bash?

I am trying to run something like this from a bash script:
HOST=foo
DIR=bar
ssh user#$HOST "
function test
{
CURHOST=$HOST
cd $DIR
mkdir -p $CURHOST
}; test"
When I run it with set -x I see that it translates to this:
+ ssh user#foo '
function test
{
CURHOST=foo
cd bar
mkdir -p
}; test
and then of course it complains about mkdir -p having no argument. Why is this and how can I set a local variable in there?
To prevent substitution, you can use either single-quotes '...' or a backslash \; for example, this command:
echo '$foo' \$foo "\$foo"
will print this:
$foo $foo $foo
and will not use the variable foo.
In your case, rather than using "..." for the whole argument, you probably should use '...' everywhere except where you specifically need substitution. So:
HOST=foo
DIR=bar
ssh user#$HOST '
function test
{
CURHOST='"$HOST"'
cd $DIR
mkdir -p $CURHOST
}; test'
Just escape the dollar sign:
echo "
function test
{
CURHOST=$HOST
cd $DIR
mkdir -p \$CURHOST
}; test"
gives you:
function test
{
CURHOST=foo
cd bar
mkdir -p $CURHOST
}; test

Resources