Problem to using bash shell with another user [duplicate] - linux

This question already has answers here:
Using variables inside a bash heredoc
(3 answers)
Trouble understanding the non-obvious use of backslash inside of backticks
(1 answer)
Closed 1 year ago.
I can't find out, how to mask the $ in the following shell statement. Any
help is appreciated!
#!/bin/bash
local result=`su -m user1 -c "sqlplus -s / as sysdba <<-EOF
SELECT cdb FROM v\$database;
exit
EOF"
`

You should single-quote EOF, e.g. <<-'EOF'
But then you also need to run the subprocess with $() instead of backquotes.
#!/bin/bash
local result=$(su -m user1 -c "sqlplus -s / as sysdba <<-'EOF'
SELECT cdb FROM v\$database;
exit
EOF")
BashFAQ/082 reports that “Backslashes (\) inside backticks are handled in a non-obvious manner”
$ echo "`echo \\a`" "$(echo \\a)"
a \a
$ echo "`echo \\\\a`" "$(echo \\\\a)"
\a \\a
# Note that this is true for *single quotes* too!
$ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar"
foo is \, bar is \\
So if you do not want to use $(...) you need 2 backslashes, i.e. \\$database and if you want to use neither $(...) nor single-quoted EOF, you need no less than 6 backslashes, i.e. \\\\\\$database

Do not use backticks. Use #(...).
local result=$(su -m user1 -c "sqlplus -s / as sysdba <<-EOF
SELECT cdb FROM v\\\$database;
exit
EOF
")
But anyway, if you do not want anything to expand, use single quotes.
local result=$(su -m user1 -c 'sqlplus -s / as sysdba <<-EOF
SELECT cdb FROM v\$database;
exit
EOF
')
And you could, redirect in the parent shell - it's going to be way simpler - and use a quoted stop word.
local result=$(su -m user1 -c 'sqlplus -s / as sysdba' <<-'EOF'
SELECT cdb FROM v$database;
exit
EOF
")

Related

Use bash to write a bash script? [duplicate]

This question already has answers here:
echo "#!" fails -- "event not found"
(5 answers)
Closed 7 months ago.
echo "#!/bin/bash\nls -l /home/" > /home/myscript.sh
bash: !/bin/bash\nls: event not found
My script should be:
#!/bin/bash
ls -l /home/
Why does it ignore the echo "" string and think that there is some sort of event? Why does it not recognize #!/bin/bash as a special word?
the same thing happens when I
echo "#!/bin/bash" > /home/myscript.sh
so it's not the new line!
echo -e "#\!/bin/bash" > /home/myscript.sh
writes the file content as:
#\!/bin/bash
Why is this simple action going miserably wrong?
From the bash manpage:
Enclosing characters in double quotes preserves the literal value of
all characters within the quotes, with the exception of $, `, \,
and, when history expansion is
enabled, !.
So either use single quotes, or disable history expansion with set +o history.
But don't use echo. Instead, do :
printf '%s\n' '#!/bin/bash' 'ls -l /home/' > /home/myscript
or
cat > /home/myscript << 'EOF'
#!/bin/bash
ls -l /home/
EOF
echo -e '#!/bin/bash\nls -l /home/' > /home/myscript.sh
a combination of -e and using single quote fixed it.

Bash discards command line arguments when passing to another bash shell

I have a big script (call it test) that, after stripping out the unrelated parts, comes down to just this using which I can explain my question:
#!/bin/bash
bash -c "$#"
This doesn't work as expected. E.g. ./test echo hi executes the only the echo and the argument disappears!
Testing with various inputs I can see only $1 is passed to bash -c ... and rest are discarded.
But if I use a variable like:
#!/bin/bash
cmd="$#"
bash -c "$cmd"
it works as expected for all inputs.
Questions:
1) I would like to understand why the double quotes don't "pass" the entire command line arguments to bash -c .... What am I missing here (that it works perfectly fine when using an intermediate variable)?
2) Why does bash discard the rest of the arguments (except $1) without any error messages?
For example:
bash -c "ls" -l -a hi hello blah
simply runs echo and hi hello blah doesn't result in any errors at all?
(If possible, please refer to the bash grammar where this behaviour is documented).
1) I would like to understand why the double quotes don't "pass" the entire command line arguments to bash -c .... What am I missing here (that it works perfectly fine when using an intermediate variable)?
From info bash #:
#
($#) Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands
to a separate word. That is, "$#" is equivalent to "$1" "$2" ....
Thus, bash -c "$#" is equivalent to bash -c "$1" "$2" .... In the case of ./test echo hi invocation, the expression is expanded to
bash -c "echo" "hi"
2) Why does bash discard the rest of the arguments (except $1) without any error messages?
Bash actually doesn't discard anything. From man bash:
If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are assigned to the positional parameters, starting with $0.
Thus, for the command bash -c "echo" "hi", Bash passes "hi" as $0 for the "echo" script.
bash -c "ls" -l -a hi hello blah
simply runs echo and hi hello blah doesn't result in any errors at all?
According to the rules mentioned above, Bash executes "ls" script and passes the following positional parameters to this script:
$0: "-l"
$1: "-a"
$2: "hi"
$3: "hello"
$4: "blah"
Thus, the command actually executes ls, and the positional parameters are unused in the script. You can use them by referencing to the positional parameters, e.g.:
$ set -x
$ bash -c "ls \$0 \$1 \$3" -l -a hi hello blah
+ bash -c 'ls $0 $1 $3' -l -a hi hello blah
ls: cannot access hello: No such file or directory
You should be using $* instead of $# to pass command line as string. "$#" expands to multiple quoted arguments and "$*" combines multiple arguments into a single argument.
#!/bin/bash
bash -c "$*"
Problem is with your $# it executes:
bash -c echo hi
But with $* it executes:
bash -c 'echo hi'
When you use:
cmd="$#"
and use: bash -c "$cmd" it does the same thing for you.
Read: What is the difference between “$#” and “$*” in Bash?

Triple nested quotations in shell script

I'm trying to write a shell script that calls another script that then executes a rsync command.
The second script should run in its own terminal, so I use a gnome-terminal -e "..." command. One of the parameters of this script is a string containing the parameters that should be given to rsync. I put those into single quotes.
Up until here, everything worked fine until one of the rsync parameters was a directory path that contained a space. I tried numerous combinations of ',",\",\' but the script either doesn't run at all or only the first part of the path is taken.
Here's a slightly modified version of the code I'm using
gnome-terminal -t 'Rsync scheduled backup' -e "nice -10 /Scripts/BackupScript/Backup.sh 0 0 '/Scripts/BackupScript/Stamp' '/Scripts/BackupScript/test' '--dry-run -g -o -p -t -R -u --inplace --delete -r -l '\''/media/MyAndroid/Internal storage'\''' "
Within Backup.sh this command is run
rsync $5 "$path"
where the destination $path is calculated from text in Stamp.
How can I achieve these three levels of nested quotations?
These are some question I looked at just now (I've tried other sources earlier as well)
https://unix.stackexchange.com/questions/23347/wrapping-a-command-that-includes-single-and-double-quotes-for-another-command
how to make nested double quotes survive the bash interpreter?
Using multiple layers of quotes in bash
Nested quotes bash
I was unsuccessful in applying the solutions to my problem.
Here is an example. caller.sh uses gnome-terminal to execute foo.sh, which in turn prints all the arguments and then calls rsync with the first argument.
caller.sh:
#!/bin/bash
gnome-terminal -t "TEST" -e "./foo.sh 'long path' arg2 arg3"
foo.sh:
#!/bin/bash
echo $# arguments
for i; do # same as: for i in "$#"; do
echo "$i"
done
rsync "$1" "some other path"
Edit: If $1 contains several parameters to rsync, some of which are long paths, the above won't work, since bash either passes "$1" as one parameter, or $1 as multiple parameters, splitting it without regard to contained quotes.
There is (at least) one workaround, you can trick bash as follows:
caller2.sh:
#!/bin/bash
gnome-terminal -t "TEST" -e "./foo.sh '--option1 --option2 \"long path\"' arg2 arg3"
foo2.sh:
#!/bin/bash
rsync_command="rsync $1"
eval "$rsync_command"
This will do the equivalent of typing rsync --option1 --option2 "long path" on the command line.
WARNING: This hack introduces a security vulnerability, $1 can be crafted to execute multiple commands if the user has any influence whatsoever over the string content (e.g. '--option1 --option2 \"long path\"; echo YOU HAVE BEEN OWNED' will run rsync and then execute the echo command).
Did you try escaping the space in the path with "\ " (no quotes)?
gnome-terminal -t 'Rsync scheduled backup' -e "nice -10 /Scripts/BackupScript/Backup.sh 0 0 '/Scripts/BackupScript/Stamp' '/Scripts/BackupScript/test' '--dry-run -g -o -p -t -R -u --inplace --delete -r -l ''/media/MyAndroid/Internal\ storage''' "

Get substring from Start Index to End Index

I'm working on this for two hours but no luck, always the "Bad substitution" error.
What I want to make (.sh script):
Read from file (names), then I'd like to substitute this name with substring of the given name by the offset and lenght which are the script arguments ($1 = offset, $2 = lenght).
It should work like this (I think) : new_user=${user:$1:$2}
-> where user is read from .txt (in while loop) and $1 and $2 are arguments of this .sc
I've highlighted the important part:
#!/bin/bash
touch postopek.log
while IFS="," read fullName userName passwordLarge
do
pass=$(perl -e 'print crypt(&ARGV[0], "password")' $passwordLarge)
new_up=${fullName:$1:$2} # important line
sudo useradd -m -p $pass -d /home/$new_up -s /bin/bash $new_up
[ $? -eq 0] && echo "Made something bla bla not important..." >> postopek.log
sudo mkdir /home/$new_up/gradivo
sudo cp -r /home/administrator/vaje/* /home/$new_up/gradivo
sudo chown -R $new_up:$new_up /home/$new_up/gradivo
done < /home/administrator/seznam.txt
The sh shell you are running the script with is probably not bash, try running it like
bash ustvari.sh 3 5
or just
/path/to/ustvari.sh 3 5
since your shebang points to bash anyway.
${parameter:offset:length} is not specified by POSIX, thus if your /bin/sh is a shell which does not support the substring syntax, you get the Bad substitution error you encountered, for example:
$ dash
$ echo ${foo:0:1}
dash: 1: Bad substitution

save wild-card in variable in shell script and evaluate/expand them at runtime

I am having trouble running the script below (in Cygwin on win 7 mind you).
Lets call it "myscript.sh"
When I run it, the following is what I input:
yearmonth: 2011-03
daypattern: 2{5,6,7}
logfilename: error*
query: WARN
#! /bin/bash
yearmonth=''
daypattern=''
logfilename=''
sPath=''
q=''
echo -n "yearmonth: "
read yearmonth
echo -n "daypattern: "
read daypattern
echo -n "logfilename: "
read logfilename
echo -n "query: "
read q
cat "$yearmonth/$daypattern/$logfilename" | grep --color $q
The output I get is:
cat: /2011-03/2{5,6,7}/error* No such
directory of file exists.
However, if I enter daypattern=25 OR daypattern=26 etc. the script will work.
Also, of course if I type the command in the shell itself, the wildcards are expanded as expected.
But this is not what I want.
I want to be able to PROMPT the user to enter the expressions as they need, and then later, in the script, execute these commands.
Any ideas how this can be possible?
Your help is much appreciated.
Try eval, this should work for the {a,d} and * cases
eval grep --color $q ${yearmonth}/${daypattern}/${logfilename}
Use quote to prevent wildcard expansion:
$ a="*.py"
$ echo $a
google.py pair.py recipe-523047-1.py
$ echo "$a"
*.py

Resources