Reputation: 163
I'm writing a script which install and configure users, zsh and zprezto. For my needs, I've written some code that should execute multiple commands as user X (with su) and with zsh shell.
The behaviour is not what I expected so I need some explanations about this code.
su admin -c zsh << EOF
echo '$USER';
echo '$SHELL';
EOF
This code was just for a test : I needed to be sure commands are executed as user admin and in zsh shell. However, output is :
root
/bin/zsh
I just don't understand : shouldn't su change user before executing command ?
I tried writing my code even with this problem and I got another strange behaviour :
cat << EOF | su "admin" -c zsh
local file="${ZDOTDIR:-$HOME}/.zpreztorc"
echo "File for $USER : ${ZDOTDIR:-$HOME}/.zpreztorc"
setopt clobber; # Do not warn when overwritting file
modules=$(cat "$file" | grep -Pzo "(?s)(zstyle ':prezto:load' pmodule\N*.)([\s\t]*'[a-z]*'[\s\t]*\\\\.)*[\s\t]*'[a-z]*'");
firstLineNumber=$(cat "$file" | grep -Fn "$(echo -n "$modules" | head -n 1)" | sed 's/^\([0-9]\+\):.*$/\1/');
lastLineNumber=$(cat "$file" | grep -Fn $(echo -n "$modules" | tail -n 1) | sed 's/^\([0-9]\+\):.*$/\1/');
for module in ${prezto_modules[@]}; do
modules="$modules \\
'$module'";
done
fileContent=$(cat "$file" | sed "$firstLineNumber,$lastLineNumber d");
echo -n "$fileContent" | head -n "$((firstLineNumber-1))" > "$file";
echo "$modules" >> "$file";
echo -n "$fileContent" | tail -n "+$((firstLineNumber))" >> $file;
cat "$file";
EOF
However, output is strange too :
cat: '': No such file or directory
cat: '': No such file or directory
cat: '': No such file or directory
grep: loaded: No such file or directory
grep: loaded: No such file or directory
grep: loaded: No such file or directory
grep: loaded: No such file or directory
grep: loaded: No such file or directory
grep: loaded: No such file or directory
grep: loaded: No such file or directory
grep: autoloaded: No such file or directory
grep: autoloaded: No such file or directory
grep: autoloaded: No such file or directory
grep: autoloaded: No such file or directory
grep: autoloaded: No such file or directory
grep: autoloaded: No such file or directory
cat: '': No such file or directory
sed: -e expression n°1, caractère 1: commande inconnue: `,'
tail: incorrect line number: « + »
File for root : /root/.zpreztorc
I tried to translate errors from french, don't know exact traductions for sed so I just let it as is. But errors themselves are not what's strange, look at my first echo line :
echo "File for $USER : ${ZDOTDIR:-$HOME}/.zpreztorc" --> "File for root : /root/.zpreztorc" Except the fact we are root, it's displayed as the last line of output. It means errors are found before executing the code, right ?
Something even more strange : if we comment the code, errors are still noticed :
su "admin" -c zsh << EOF
# modules=$(cat "$file" | grep -Pzo "(?s)(zstyle ':prezto:load' pmodule\N*.)([\s\t]*'[a-z]*'[\s\t]*\\\\.)*[\s\t]*'[a-z]*'");
EOF
The output is :
cat: '': No such file or directory
How could you explain that ? Thanks
Upvotes: 0
Views: 183
Reputation: 47099
Here documents with << MARKER
will be interpreted as double quotes strings, while << 'MARKER'
will be interpreted as single quoted strings:
su admin -c zsh << 'EOF'
echo "$USER" "this is admin"
EOF
While using << MARKER
is expanded before send as stdin
su admin -c zsh << EOF
echo "$USER" "this is the current user single quotes doesn't prevent it"
echo '$USER' 'This is still expanded before send as stdin to zsh'
EOF
See man bash | grep --max-count=1 '<<' -A 11
for more information:
<<[-]word
here-document
delimiter
No parameter and variable expansion, command substitution, arithmetic
expansion, or pathname expansion is performed on word. If any charac‐
ters in word are quoted, the delimiter is the result of quote removal
on word, and the lines in the here-document are not expanded. If word
is unquoted, all lines of the here-document are subjected to parameter
expansion, command substitution, and arithmetic expansion, the charac‐
ter sequence \<newline> is ignored, and \ must be used to quote the
characters \, $, and `.
Upvotes: 2