Shell Issues
Debris
-
双引号包裹下引用变量,其中的换行符号(不是转义)会被解析,否则会被直接忽视(不会被替换为
\n
),例如:$pre=" I Love You " echo $pre # 输出一行内容 echo "$pre" # 输出多行内容
-
引用变量时,若其中存储的是字符串且对字符串的转义有严格要求,则形如
"$var"
的变量引用等价于其内容外套一层双引号,该引用过程只发生一次转义。
Solutions
重定向权限与 tee
sudo echo $pre_source > /etc/apt/sources.list
该语句会提示权限不够,因为 >, >>
等重定向符也需要 root 权限。
可以利用 tee
命令进行重定向:
echo "$pre_source" | sudo tee /etc/apt/sources.list
tee
相当于 >
,tee -a
相当于 >>
。
Here Document
为"here document"的分隔符加双引号,会让其不解析输入内容中的变量表达式 $<str>
:
<cmd> << "EOF"
$abc
EOF
终止符仍为 EOF
不加引号,且必须顶格。
#!/bin/bash
# 设置 root 密码
function root_passwd(){
passwd -d root > /dev/null
passwd root <<- EOF > /dev/null
"$pre_passwd"
"$pre_passwd"
EOF
# EOF 好像必须挤在开头, 且后面不能跟别的东西, 包括注释
}
<<-
能让传入的数据忽略制表符 \t
。
但是在编辑器 VSC 中的 tab 按键产生的缩进实际上是多个空格,而 Vim 下的 tab 产生的缩进用的才是制表符 \t
。
由于 <<-
只会忽略制表符 \t
而不是空格,因此此处输入的密码前面有数个空格。
同时 hd 下的双引号并不会被解析,因此密码前后各有一个双引号。
shell
与 echo
的转义
shell 真是转义地狱:
#!/bin/bash
pre="
echo \"
# for i in {0..255}; do print -Pn \\\"%\\\${i}F\\\${(l:3::0:)i}%f \\\" \\\${\\\${(M)\\\$((i%8)):#7}:+\\\$'\\\\n'}; done
\"
"
执行 su - festu -c $pre
换行会被转义后输出。
解释:
echo
会对传入的特殊字符转义一次。其转义范围包括 \n, \t
等特殊字符,但不包括 $, `, "
。
据此来剖析一下上述语句的转义流程。
首先是 $pre 变量被创建,shell 解析双引号括起的字符串时,会对其中的 $, `, "
进行一次转义。因此存入 $pre 中的换行周边的内容为 \\n
。
接着变量作为参数被给命令 su
,这个过程原样传递。传入的内容又作为 shell 命令被解析,由于换行符仍然是被双引号括起的字符串,因此再次发生转义,\\n
变为 \n
作为参数被传给 echo
命令,然后又被转义,最后变成了换行被输出。
前两次转义对 $, `, "
同样生效,同时又作用于 \\\\n
中的反斜杠,换行符也牵连着被转义了两次(当然这里只是说起了转义的效果,实际上 shell 解析双引号括起的字符串时并不解析换行符本身的转义即 \n
)
而 echo 的转义只对换行符本身起效,因此换行符最终被转义了三次,区别于 shell 特殊字符的两次。
解决方案:为 echo
添加 -E
参数取消它自带的特殊字符转义:
pre="
echo -E \"
# for i in {0..255}; do print -Pn \\\"%\\\${i}F\\\${(l:3::0:)i}%f \\\" \\\${\\\${(M)\\\$((i%8)):#7}:+\\\$'\\\\n'}; done
\"
"
值得一提的是 echo 与 shell 的转义非常复杂,反复混用可能会非常混乱,造成"转义地狱"的糟糕情况(虽然 shell 早就是了),而且研究起来价值不高,因此不建议反复混用或深究。
(就深究一点点 = =)
例如 shell 并不解析单引号字符串中的转义,因此理论上下面的脚本也能输出 \n
:
pre="
echo '
# for i in {0..255}; do print -Pn \\\"%\\\${i}F\\\${(l:3::0:)i}%f \\\" \\\${\\\${(M)\\\$((i%8)):#7}:+\\\$'\\\\n'}; done
'
"
对于 \\\\n
,变量赋值时是双引号字符串,被转义一次,变为 \\n
;执行字符串表示的命令时是单引号字符串,因此不做转义;传给 echo
后被转义一次,变为 \n
。
理论上该语句只对换行符做了两次转义,最后输出的只是字符 \n
。但实际上输出时仍发生了换行。
除此之外还有很多奇怪的问题...总之不建议深究。
最后作出一个结论,如果是全是使用双引号的需要二次转义的场景(写入配置时),使用 echo -E
重定向
写脚本时看能希望在脚本运行时,永久将『标准错误输入』重定向至某处,利用 exec
可实现:
exec 2>~/errLog # 重定向至文件
exec 1>/dev/null # 重定向至空
exec 1>/dev/tty1 # 将标准输出重定向到 tty1 终端
标准输入输出设备标识符:
/proc/self/fd/0:标准输入 0
/proc/self/fd/1:标准输出 1
/proc/self/fd/2:标准错误输出 2
与另一个用户通信
write bob > /dev/tty2
与同一系统下的另一用户 bob 通信。
su
指定用户执行命令
在交互式 shell 中 su 指定用户执行命令并不能准确读取用户的环境变量如 ~
,但在脚本中运行时可以。
su - festu -c "
cd ~;
mkdir -p Tmp;
cd Tmp;
echo $user >> tmp.txt
"
# tmp.txt 中的内容为"festu",有换行可以不要分号
# sudo 执行 Shell 脚本时也能读取正确的用户变量
奇怪的转义特性(感觉是 BUG):
user="festu"
su - festu -c "echo \$user" # 输出空行
su - festu -c "echo $user" # 输出"festu"
su - festu -c "echo \"\$user\"" # 输出"festu"
su - festu -c "echo \"\\\$user\"" # 输出"$user"
当试图用 echo
向配置文件中写入原生的 $HOME
等字符串时,需要注意转义的次数。
Vim 操作与转义脚本
使用场景:
在编写系统配置脚本时,希望用 echo '...' > conf
语句去快速创建配置文件,由于单引号内的字符是绝对的原始字符串,因此无需特殊处理。但如果使用了双引号,就要考虑对原配置文件进行转义处理。
更进一步,脚本中可能需要指定用户执行配置命令,以利用用户的环境变量,如家目录 ~
:
su - <user> -c "
echo \"
...
\" > ~/conf
"
此时就需要对配置文件的内容进行二次转义。
转义脚本:
#!/bin/bash
if [ -z $1 ];then
echo "${0} fileName [times]"
exit 1
fi
if [[ $1 == "-h" || $1 == "--help" ]];then
echo "Escape special characters in \"Double Quotes\" of shell scripts (for one time or more)."
exit 1
fi
if [ -z $2 ];then
times=1
else
times=$2
fi
for ((i=0;i<${times};i++))
do
vim $1 <<- EOF
:%s/\\\\/\\\\\\\\/g
:%s/\"/\\\\\"/g
:%s/[\$]/\\\\\$/g
:%s/\`/\\\\\`/g
:wq
EOF
done
解析:
非常的美妙不是吗。
首先在终端中键入的(即保存在文本中的) \\
被 shell 解析时发生第一次转义。
对于表达式 s/<regex>/<string>/g
,首先他会直观地替换 vim 显示的内容(尤其是对于反斜杠)。
表达式中 regex 部分被视为正则表达式,string 部分被视为普通字符串,\\
由于其特殊性,不论在 regex 部分还是 string 部分都会且仅会被转义一次变为 \
。
由于替换的"直观性",在 vim 中看到的的所有 \
会被挨个替换
而对于 $
字符,由于在正则表达式中它代表结尾,因此在 regex 中表示一个 $
,需要用 \$
。
由于这是转义一次后的结果,因此写在脚本中的字符串为 \\$
;或者用 [$]
即 [\\$]
也可以。而在 string 部分 $
是普通字符,因此不需要转义。
对于反引号 `
,由于在 shell 脚本中它有特殊含义,因此首先需要用 \\\`
来转义,使其在被 shell 解析后仍为反引号,而不是作为命令解析器被处理。
从这个角度来讲,$
也应该额外添加反引号做转义,事实上这么做确实是更好的。
回到反引号,在作为 vim 命令模式的参数传入时它和美元符号一样没有特殊含义,因此不再需要额外的转义。
标签:pre,shell,双引号,su,echo,Shell,转义,Issues From: https://www.cnblogs.com/Forest-set-you/p/17736370.html