Skip to content

shell笔记 #90

Open
Open
@uniquejava

Description

shell

变量与变量展开

hello=world
echo $hello
hello="i am world"
echo $hello
echo hello world
echo -n "enter your name:" (-n去掉行尾的回车)
read name
echo $name
  1. $是取变量的值,所以总是出现在=号的右边
  2. =号前后不能有任何空格
  3. =号后面的字符串如果不包含空格可以不加""
  4. echo后面字符串即使包含空格也可不加"", 如果要正常输出变量中的换行符,则必须加双引号
  5. shell视所有变量为字符串类型.
  6. 单引号不展开变量, 仅原样输出字符串, 双引号会展开变量.
  7. 强制展开变量的办法:嵌套使用单引号,或者整体使用双引号或者拼字符串
    $ sed -n '1 c$HOME' package.json(不会展开变量)
    $HOME
    $ sed -n "1 c$HOME" package.json(整体使用双引号)
    /home/cyper
    $ sed -n '1 c'$HOME'' package.json(嵌套使用单引号)
    /home/cyper
    $ sed -n '1 c'$HOME package.json(拼字符串)
    /home/cyper

输出重定向

3个特殊的文件描述符0 stdin, 1 stdout, 2 stderr.

格式:command FILE_DESCRIPTOR>outputFileName

foo 1>ok.txt 2>error.txt

因为foo命令不存在,所以出错信息输出到了error.txt中,注意到同时生成了一个空的ok.txt文件, 其中标准输出重定向1>可以简写成>

怎么将正常的及错误的消息输出到同一文件foo 1>ok.txt 2>&1, 即使用&1来表示文件描述符为1对应的输出文件,在这里它们同时指向ok.txt.

引号

'原样输出
"会先做expansion

参数

$HOME 打印出/home/cyper
$0: shell script的名字
$#: 参数个数
$$: 运行脚本时的process ID
$1,$2, ...传递给脚本的参数
$*: 所有参数使用$IFS分隔(默认为空格)
$@: 同$*但总是使用空格分隔
$?: 命令的退出状态,0成功,非0失败
PS1,PS2: 命令提示符

快速测试

    $ IFS=’‘
    $ set foo bar bam
    $ echo$@“
    foo bar bam
    $ echo$*“
    foobarbam
    $ unset IFS
    $ echo$*“
    foo bar bam

条件测试

if test -f fred
then
...
elif test -d fred; then
...
else
...
fi

if [ -f fred.c ]
then
...
fi

if [ -f fred.c ]; then
...
fi

[]和其中包含的测试条件必须用空格隔开

条件比较

string comparison result
str1 = str2 string equal
str1 != str2 string not equal
-n str string is not null
-z str string is null(an empty string)
num1 -eq num2 exprs are equal
eq, ne, gt, ge, lt, le ...
! expr1 is false?
-d file is directory?
-e file exist?
-f file is file?
-r file is readable?
-w file is writable?
-x file is executable?
-g file if set-group-id is set on file?
-u file if set-user-id is set on file?

You may be wondering what the set-group-id and set-user-id (also known as set-gid and set-uid) bits are. The set-uid bit gives a program the permissions of its owner, rather than its user, while the set-gid bit gives a program the permissions of its group.The bits are set with chmod,using the s and g options.The set-gid and set-uid flags have no effect on files containing shell scripts, only on executable binary files.

特殊情况

if [ $timeofday = "yes" ]这段可能有问题, 当timeofday为空的时候if [ = "yes"]语法不合规,最好写成if [ "$timeofday" = "yes" ]

for

for i in foo bar 43
do
  echo $i
done

for i in $(ls f*.sh); do
  lpr $file
done

while

echo "Enter password"
read trythis
while [ "$trythis" != "secret" ]; do
  echo "sorry, try again"
  read trythis
done

util

who查看当前系统有哪个登录用户, 比如who|grep cyper返回0, 而who|grep green返回1, 这里我们只关心script的执行情况(0 or 1), 不关心stdout.

# 如果参数1对应的用户没有出现, 就等待60s后再次检测
until who | grep "$1" > /dev/null
do
  sleep 60
done

# 响铃
echo -e '\a'
echo "**** $1 has just logged in ****"

exit 0

case

基本结构

case "$x" in
  pattern | pattern2 ) statements;;
  * )  echo "default case";;
esac

例子:

#!/bin/sh
echo "Enter your name?"
read timeofday
case "$timeofday" in
  yes | y | Yes | YES )
    echo "Good morning"
    echo "It's cool"
    ;;
  [nN]*)
    echo "Good afternoon"
    ;;
  *)
    echo "sorry"
    exit 1
    ;;
esac

exit 0

AND

打印出hello in else

touch file_one
rm -rf file_two
if [ -f file_one ] && echo "hello" && [ -f file_two ] && echo " there"
then
  echo "in if"
else
  echo "in else"
fi

OR

打印出hello in if

rm -f file_one
if [ -f file_one ] || echo "hello" || echo "there"
then
  echo "in if"
else
  echo "in else"
fi

Statement Blocks

可以用AND/OR选择性的执行语句块

get_confirm && {
  echo "hello"
  echo "dome some other thing"
}

braces and brackets

braces()中的command在subshell中执行,在期间修改的env var不会影响current shell(比如执行了cd命令).在brackets{}中包含的command在当前shell中执行.

set -e

遇到错误即退出, 不继续执行后续脚本

set -o noclobber

打开此选项是为了防止使用重定向符>意外覆盖文件,如果要强制覆盖,使用>|.

Here document

用来快速提供多行输入,常用于交互式命令比如telnet, ftp.

command << terminaor [input ...]
或者
command <<- terminator [input ...]

每次读一行,直到读到某行是terminator时结束,<<-会把input中每行前面的tabs去掉。
登录到一个远程机器并把某文件mail给本机。

$ telnet << END
> open 190.100.2.1
> gregl password
> mail -s "remote userlogs" gregl@190.100.2.2 < /var/log/userlog
> quit
> END

substitution

文件名替换: 使用w*,?,[a-z],
命令替换: $(command), $(< filename)

* 0或多个字符
? 单个字符
[a-z0-9] 范围内单个字符
[!0-9] 范围外单个字符,和正则^不一样
$(command) 执行command并把输出结果作为其它命令的参数
$(< filename) 将filename的内容作为其它命令的参数
echo '*' 如果需要转递特殊字符做为参数,需要使用引号或\以防止该符号被shell解释.
echo \* 同上
echo "*" 同上

Date

DATE=`date +%Y-%m-%d`
$(date +%F)
DATE=`date +%Y-%m-%d:%H:%M:%S`

其中%Y-%m-%d <==> %F, 输出2017-04-26

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions