首页 > 系统相关 >Shell Issues

Shell Issues

时间:2023-09-28 19:11:34浏览次数:28  
标签:pre shell 双引号 su echo Shell 转义 Issues

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 下的双引号并不会被解析,因此密码前后各有一个双引号。

shellecho 的转义

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

相关文章

  • linux下shell脚本实现wordpress搭建
    wordpress_auto_install.sh  #!/bin/bashuser=$(whoami)functionwordpress_auto_install(){if[$user=="root"];thenecho"前提:调整系统配置,如关闭selinux、firewall等!"sed-i's/SELINUX=enforcing/SELINUX=disabled/'/etc/selinux/......
  • Shell Scripts
    ShellScriptsShell编程笔记基本语法模块指定脚本解析器#!/bin/bash#!/bin/shbash是最常用的解析器,sh等其他shell解析器与bash有语法上的区别变量变量名:字母、数字、下划线,数字不可开头变量赋值:user="festu"user=festu"festu" #festufestuuser=festu#......
  • 玩转Redhat Linux 8.0系列 | 使用BASH SHELL执行命令
    今天继续分享一些RedhatLinux8.0的知识,记得关注,会一直更新~基本命令语法GNUBourne-AgainShell(bash)这一程序可以解读用户键入的命令。当您准备好执行命令时,请按Enter键。在单独的行上键入每个命令。系统会显示命令输出,然后显示下一shell提示符。[user@host]$whoamiuser[u......
  • [Linux] shell文本处理记录 - 查找、增删特定行及附近行
    转:https://blog.csdn.net/wy_hhxx/article/details/127416595查找username所在行并删除此行,输出到新文件sed'/username/,+d'04filename.log>04filename_new.log 目录1.grep查找关键字所在行号、查找关键字前后行2.sed删除指定行及其前后若干行3.sed在匹配行前或后添......
  • Powershell 获取AD Certificate 详细信息
    get-aduser-SearchBase$ou-Filter*-Propertiesdisplayname,usercertificate|ForEach-Object{$displayname=$_.displayname$_|select-ExpandPropertyusercertificate|ForEach-Object{$cert=[System.Security.Cryptography.X509Certifi......
  • shell遍历比较文件夹下文件md5值
    #!/bin/bashCURRENT_DIR=$(cd$(dirname$0);pwd)SOURCE_DIR="$CURRENT_DIR/python_data"TARGET_DIR="$CURRENT_DIR/out_bin"cd$SOURCE_DIR>python.md5forfilein$(ls$SOURCE_DIR|grep"data")dosource_file=${SOURCE_......
  • 掌握Shell用户管理,让你的系统运行更顺畅!
    用户帐号帐号操作主要是增、删、改、禁。Linux系统提供了底层的 useradd, userdel 和 usermod 来完成相关操作,也提供了进一步的简化封装:adduser, deluser。为了避免混淆,咱们这里只介绍最底层的指令,这些指令设计上已经够简洁明了方便。由于只有系统管理员才能创建新用户,请确......
  • How to add a string that contains whitespace to array in shell script All In One
    HowtoaddastringthatcontainswhitespacetoarrayinshellscriptAllInOneIhavetriedsomewaystoaddastringwhichcontainwhitespacetoarrayinshellscript,butfailed.stringvariablesconcatenate#!/usr/bin/envbashstr1="hello&qu......
  • linux-Shell将命令行终端输出结果写入保存到文件中
    (一)将输出与错误写到同一个文件(1)方法1#!bin/bashjava-jarhbase-example.jar2>&1|teehbase_log.txt说明:0,1,2:在linux分别表示标准输入、标准输出和标准错误信息输出。tee默认为写入覆盖,-a参数表示追加内容。#!bin/bashjava-jarhbase-example.jar2>&1|tee-ahbase_......
  • Linux shell编程学习笔记1:关于shell的前世今生
    一、什么是Shell?Shell英文单词的原意是“外壳”,在计算机领域专指在操作系统(OperatingSystem)外层,提供用户界面(UserInterface)的程序,主要负责将用户的命令(Command)转化为操作系统可识别的指令(Instruction)。二、UnixshellUnix诞生于1969年,是最早提供shell,从而将操作系统和用户界面......