Get and use a password with special characters in Bash shell - linux

I'm getting some troubles to use a password with special characters such as $ in a bash shell script.
My shell script is :
read -s -p "Password : " bindDNPass
ldapadd -H ldap://localhost -x -w $bindDNPass -D "dn=cn=Admin" -f /tmp/file.ldif
And the password could be something like $Something18$.
Well, the command
ldapadd -H ldap://localhost -x -W -D "dn=cn=Admin" -f /tmp/file.ldif`
asks for my $Something18$, and works fine.
But if I try
ldapadd -H ldap://localhost -x -w $Something18$ -D "dn=cn=Admin" -f /tmp/file.ldif
it doesn't work. I guess it's trying to resolve the variable $Something18, so I tried with \$Something18$, \$Something18\$, \\\$Something18$, ... but it keeps on failing...
How can I do? (Without changing my password...)

Put it in double-quotes and escape the $ symbol avoid special interpretation from the shell,
ldapadd -H ldap://localhost -x -w "\$Something18\$" -D "dn=cn=Admin" -f /tmp/file.ldif
(or) [more recommended]
Enclose it within single-quote to let the shell treat it as a literal string without expanding it,
ldapadd -H ldap://localhost -x -w '$Something18$' -D "dn=cn=Admin" -f /tmp/file.ldif
From the man bash page,
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, !. The
characters $ and retain their special meaning within double quotes. The backslash retains its special meaning only when followed by one of the following characters: $, `, ", \, or . A double quote may be quoted within double quotes by preceding it with a backslash. If enabled, history expansion will be performed unless an ! appearing in double quotes is escaped using a backslash. The backslash preceding the ! is not removed.

I see two potential problems with how you're reading and using the password:
When you use the read command without the -r option, it'll try to interpret escape (backslash) sequences, which may cause trouble.
When you use a variable without wrapping it in double-quotes, it'll try to split the value into separate words and also try to expand any wildcards into a list of matching filenames. This can cause massive confusion, so you should almost always double-quote variable references.
Fixing these potential problems gives this script snippet:
read -rs -p "Password : " bindDNPass
ldapadd -H ldap://localhost -x -w "$bindDNPass" -D "dn=cn=Admin" -f /tmp/file.ldif
...But, while you should do both of these mods to make your script more robust, neither of these will change how it handles the password $Something18$. In fact, when I tried your original snippet with that password, it got passed to ldapadd correctly. If your actual password has some other special characters in it (or you've played with the value of IFS), these might help; otherwise, there's something else going on.
If your password still doesn't work after these fixes, try putting set -x before the ldapadd command (and set +x after) so it'll print what's actually being passed to ldapadd. Well, it'll print it in a possibly confusing form: it'll print an equivalent command to what's actually being executed, which means it'll add quotes and/or escapes to the password parameter as necessary so that you could run that command and it'll do the same thing. When I tried it with $Something18$, it printed:
+ ldapadd -H ldap://localhost -x -w '$Something18$' -D dn=cn=Admin -f /tmp/file.ldif
...where the single-quotes mean that what's inside them is passed directly, with no parsing. It could also have printed any of the following equivalent commands:
+ ldapadd -H ldap://localhost -x -w \$Something18\$ -D dn=cn=Admin -f /tmp/file.ldif
+ ldapadd -H ldap://localhost -x -w "\$Something18\$" -D dn=cn=Admin -f /tmp/file.ldif
+ ldapadd -H ldap://localhost -x -w $'$Something18$' -D dn=cn=Admin -f /tmp/file.ldif
so you have to take what it prints, and figure out how that'd be parsed by bash, in order to figure out what's actually being passed to ldapadd. But at least it'll give you some information about what's actually happening.
Oh, and you may notice that the DN argument isn't being double-quoted. That's because it doesn't contain any special characters, so the double-quotes aren't doing anything, so it just left them off.

Related

Bash escape character

I have this very reduced example of a bash command, where I want the $ sign escaped.
So the command :
su -m user -c "echo $test"
should print out:
$test
a simple \$test does not work unfortunately. I tried lots of other stuff but still couldn't find a solution. Any suggestions ?
Put it in single quotes rather than double quotes.
su -m user -c 'echo \$test='
The single quotes keep the variable from being expanded by the original shell. The backslash then escapes the dollar sign in the shell run by su.
See Difference between single and double quotes in Bash
In answer to the comment, you can switch to double quoting to get single quotes into the string.
su -m user -c 'echo \$test='"'1'"

View passed parameters

I have the following code snippet, I believe that the $env_vars variable isn't what I expect it to be.
Is there a program / script that I could substitute docker with, that would then print the entire command line to terminal? So I can debug what's wrong with my command line and then substitute docker back.
docker run \
-v $filePath:/script \
-v $uuid:/secrets \
$env_vars \
$containerImage \
bash /script/entry.sh
So I could have something like
someotherexe run \
-v $filePath:/script \
-v $uuid:/secrets \
$env_vars \
$containerImage \
bash /script/entry.sh
and it would print
someotherexe run -v somepath:/script -v uuid:/secrets etc...
You can replace docker with echo:
echo run -v somepath:/script -v uuid:/secrets etc...
Printing arguments with echo can be misleading, since it just mashes its arguments together with spaces in between. What if one of the arguments contains spaces, or nonprinting characters?
The common way to do this is to put set -x before the command, which makes the shell print its favorite (sometimes cryptic) representation of what the command and its arguments are as it executes it.
I also sometimes use something like this:
printargs() {
if [ $# -eq 0 ]; then
echo "printargs did not get any arguments"
else
echo "printargs got $# argument(s):"
printf " '%s'\n" "$#" | LC_ALL=C cat -vt
fi
}
This prints nonprinting and non-ASCII characters in weird-but-visible formats. For example:
carriagereturn=$'\r'
tab=$'\t'
$ echo –x "spaces and control characters$tab$carriagereturn"
–x spaces and control characters
$ printargs –x "spaces and control characters$tab$carriagereturn"
printargs got 3 argument(s):
'M-bM-^#M-^Sx'
'spaces and control characters^I^M'
What's happening here: for one thing, with the printargs version it's clear which spaces are part of arguments, and which are separators between arguments. The "–x" has a unicode en-dash instead of the plain ASCII hyphen that command-like tools recognize as indicating options; the cat -vt part converts its UTF-8 representation into a series of "meta" (M-something) characters. The carriage return and tab are ASCII control characters, specifically control-I and control-M, so it prints them using ^ as shorthand for "control-".

Escape quotes inside su -c

I would like to pass a path potentially containing whitespace into an su -c command inside a .sh script:
su -c "xdg-desktop-menu install ${DesktopFile}" -m "${Username}"
$DesktopFile is my path that needs to get escaped. I tried many variations with ' inside "", with \" and several braces, but I have not come up with a solution. Interestingly enough, xdg-desktop-icon works fine, but xdg-desktop-menu dies at the whitespace. Any suggestions?
If you're using bash, printf '%q' can be used to escape special characters when quoting is insufficient.
su -c "xdg-desktop-menu install $(printf '%q' "$DesktopFile") -m $(printf '%q' "$Username")"
Notice that $(printf...) doesn't need quotes, but "$DesktopFile" does. The variable needs to be passed to printf safely, so it has to be quoted. printf '%q''s output is guaranteed to be immune to word splitting and globbing, so it doesn't need quotes. (Although, if you wanted to quote it, you could. It would need \" on either side.)
i can't make out if this is a bug in xdg-desktop-menu, since it works in xdg-desktop-icon, nevertheless there is a workaround I am now using:
cd "$PathWithSpaces"
su -c "xdg-desktop-menu install $DesktopFilenameWithoutSpaces" -m "$Username"
PathWithSpaces contains DesktopFilenameWithoutSpaces, seems to work reliably.

Why does bash insert additional quotes

I need to pipe an expression including single quotes to a command, but bash inserts loads of extra quotes which breaks my command. As a really simple example take:
#!/bin/bash -x
echo 'EXPRESSION' | more
which gives:
+ echo EXPRESSION
+ more
EXPRESSION
As I want the single quotes to be displayed, I must escape them:
#!/bin/bash -x
echo \'EXPRESSION\' | more
Which now gives me:
+ echo ''\''EXPRESSION'\'''
+ more
'EXPRESSION'
So within the script, I get this bizarre ''\''EXPRESSION'\''' thing. The command I am piping the expression to is an executable that interacts with a document management system, and expects a specific format—which includes single quotes around EXPRESSION and not ''\'' and '\'''.
Is there any way to stop bash from adding the additional quotes and backslashes? I've messed around with strings and eval etc., but have failed to get rid of those additional quotes.
You can also try it with double quotes like this,
echo "'EXPRESSION'"|more
Output will be,
'EXPRESSION'
The /bin/bash -x is producing the top 2 lines. Your code produces the 3rd line. If you want you can just remove the -x and you should see it in a better way.
The above answer from Skynet works just fine, but with the -x option, it still shows 3 lines. It's just what the -x does.

Error in tmux's configuration file

In .tmux.conf file, I have this line :
set -g status-left '#(iwgetid | cut -d '"' -f 2)'
which throws an error when I run tmux :
/home/user/.tmux.conf:8: invalid or unknown command: set -g status-left '#(iwgetid | cut -d '"' -f 2)'
The error is at the " because when I change it with a , instead, tmux doesn't throw an error. Of course, it doesn't behave as expected also.
Can someone tell me how I can solve this problem?
I already tried escaping " and ' without luck.
Start with what you want the shell to get:
iwgetid | cut -d '"' -f 2
Wrap it in #() so that it will be treated as a shell command in the expansion of status-left:
#(iwgetid | cut -d '"' -f 2)
Embed this as a string that tmux can parse. You tried to put single quotes around it:
'#(iwgetid | cut -d '"' -f 2)'
The problem is that tmux will interpret the first of the “inner” single quotes and the first of the “outer” single quotes as a single string segment, leaving the following double quote at the top-level where it now forms an unterminated string segment; the error message is not terribly clear in this situation.
To fix the problem you need to properly quote the format string (which embeds the shell command) for your chosen tmux quote characters. You might do it like this:
set -g status-left '#(iwgetid | cut -d '"'\"'"' -f 2)'
But, that is a bit hard to read, so you might consider switching your shell command to something more easily tmux-quotable:
iwgetid | cut -d \" -f 2
Which you can embed in status-left with tmux single quotes like this:
set -g status-left '#(iwgetid | cut -d \" -f 2)'
Or with tmux double quotes like this:
set -g status-left "#(iwgetid | cut -d \\\" -f 2)"
Keep in mind that tmux quoting is similar to, but not identical to Bourne-style shell quoting (e.g. top-level backslash escaping does not work the same). This quoting system is used in the config file, command-prompt (e.g. Prefix :), the confirm-before and if-shell command parameters, and the command templates for the choose- commands.

Resources