欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Shell 笔记总结

程序员文章站 2022-03-04 13:00:21
...

Shell 笔记总结

  • 种类

    • Bourne Shell (/usr/bin/sh 或 /bin/sh)
    • Bourne Again Shell (/bin/bash)
    • C Shell (/usr/bin/csh)
    • K Shell (/usr/bin/ksh)
    • Shell for Root (/sbin/sh)
  • Shell 脚本

#!/bin/bash

echo 111

Shell 变量

变量名和等号之间不能有空格

for file in `ls /etc`; do
# 或 for file in $(ls /etc); do
	echo "${file}.log"
done

your_name="qinjx"
echo $your_name
echo ${your_name} # 推荐


# 只读变量
myURL="http://www.google.com"
readonly myURL
myURL="http://www.baidu.com" # /bin/sh: NAME: This variable is read only.

# 删除变量
unset ${your_name}
echo ${your_name}

# 变量作用域
## 局部变量
## 环境变量
## Shell 变量


# 字符串
## 单引号(原样输出)
str='this is a string'

## 双引号(可以有变量,可以出现转义字符)
your_name='runoob'
str="Hello, I know you are \"$your_name\"! \n"
echo -e $str

## 字符串操作

### 获取字符串长度
string="abcd"
echo ${#string} # 4
expr length "$string"

### 提取子字符串
string="runoob is a great site"
echo ${string:1:4} # unoo

### 查找子字符串
string="runoob is a great site"
echo `expr index "string" io` # 4  查找字符 i 或 o 的位置(哪个字线先出现就计算哪个)

# 数组
## 数组元素用空格符号分割开
## 形式 数组名=(值1 值2 ... 值n)

array_name=(value0 value1 value2 value3)

array_name=(
value0
value1
value2
value3
)

array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
array_name[n]=valuen # 可惜不使用连续的下标,且下标的范围没有限制

## 读取数组
## ${数组名[下标]}

value=${array_name[n]}
echo ${array_name[@]} # 获取数组中的所有元素

## 获取数组的长度
length=${#array_name[@]}
length=${#array_name[*]}

lengthn=${#array_name[n]} # 单个元素的长度

# 注释
## 行注释
# 这是一个注释
#### 用户配置区 开始 ####
#### 用户配置区 结束 ####

## 多行注释(EOF 可以使用其它符号)
:<<EOF
注释内容 ...
注释内容 ...
注释内容 ...
EOF

:<<'
注释内容 ...
注释内容 ...
注释内容 ...
'

:<<!
注释内容 ...
注释内容 ...
注释内容 ...
!


#
##
%
%%

  • # ## 表示从左边开始删除
    • # 表示从左边删除到第一个指定的字符
    • ## 表示从左边删除到最后一个指定的字符
  • % %% 表示从右边开始删除
    • % 表示从右边删除到第一个指定的字符
    • %% 表示从左边删除到最后一个指定的字符
var="http://www.aaa.com/123.htm"

echo ${var#*//} # www.aaa.com/123.htm
echo ${var##*/} # 123.htm
echo ${var%/*}  # http://www.aaa.com
echo ${var%%/*} # http

echo ${var:0:5} # http
echo ${var:7}   # www.aaa.com/123.htm
echo ${var:0-7:3} # 123
echo ${var:0-7}   # 123.htm
# 左边的第一个字符是用 0 表示,右边的第一字符用 0-1 表示


expr 5+6 # 5+6
expr 5 + 6 # 11
expr 5 * 6 # 输出错误
expr 5 \* 6 # 30




read -p "input a val: " a
read -p "input b val: " b
r=$[a+b]
echo "result = ${r}"



传递参数

# $0 执行的文件名

# $1 第一个参数
# $2 第二个参数
# $n 第 n 个参数

# $# 参数个数
# $* 以一个单字符串显示所有向脚本传递的参数
# [email protected] 在引号中返回每个参数

# $$ 脚本运行的当前进程 ID 号
# $! 后台运行的最后一个进程的 ID 号

# $- 显示 Shell 使用的当前选项(与 set 命令功能相同)
# $? 显示最后命令的退出状态(0 表示没有错误,其它值表示有错误)
# test.sh
echo '--- $* 演示 ---'
for i in "$*"; do
	echo $i
done

echo '--- [email protected] 演示 ---'
for i in "[email protected]"; do
	echo $i
done

$ chmod +x ./test.sh
$ ./test.sh 1 2 3
--- $* 演示 ---
1 2 3
--- [email protected] 演示 ---
1
2
3
if [ -n "$1" ]; then
	echo '包含第一个参数'
else
	echo '没有包含第一个参数'
fi

[ $var -eq 0 ] 变量是否为 0
[ -e $var ] 文件是否存在
[ -d $var ] 是否目录
[[ $var1 = $var2 ]] 两个字符串是否相同

[] 常常可以使用 test 命令来代替

数组

E="Hello"
F="Old Man"
array_name=(A B "C" D $E $F) # ("A" "B" "C" "D" "Hello" "Old" "Man")
array_name[7]="Very Good"  # ("A" "B" "C" "D" "Hello" "Old" "Man" "Very Good")

i=2
echo $array_name[0]
echo $array_name[$i]




# 遍历
#!/bin/bash
arr=(1 2 3 4 5 6 7 8 9 10)
for a in ${arr[*]}
do
	echo $a
done


#!/bin/bash
my_arry=(a b "c","d" abc)
echo "-------FOR循环遍历输出数组--------"
for i in ${my_arry[@]};
do
  echo $i
done

echo "-------::::WHILE循环输出 使用 let i++ 自增:::::---------"
j=0
while [ $j -lt ${#my_arry[@]} ]
do
  echo ${my_arry[$j]}
  let j++
done

echo "--------:::WHILE循环输出 使用 let  "n++ "自增: 多了双引号,其实不用也可以:::---------"
n=0
while [ $n -lt ${#my_arry[@]} ]
do
  echo ${my_arry[$n]}
  let "n++"
done

echo "---------::::WHILE循环输出 使用 let m+=1 自增,这种写法其他编程中也常用::::----------"
m=0
while [ $m -lt ${#my_arry[@]} ]
do
  echo ${my_arry[$m]}
  let m+=1
done

echo "-------::WHILE循环输出 使用 a=$[$a+1] 自增,个人觉得这种写法比较麻烦::::----------"
a=0
while [ $a -lt ${#my_arry[@]} ]
do
 echo ${my_arry[$a]}
 a=$[$a+1]
done


# Bourne shell(原生kernel下)下不支持数组,只能通过模拟来实现类似数组功能
#!/bin/sh
#注意不是/bin/bash
echo "##############使用eval函数###############"
echo "使用参考:"
echo "http://www.runoob.com/linux/linux-comm-eval.html"
eval a1=bili
eval a2=nico
eval a3=yama

for i in 1 2 3 ; do
  eval echo "\$a$i"
done

#!/bin/sh
#注意不是/bin/bash
echo "##########################################"
echo "指令参考:"
echo "http://www.runoob.com/linux/linux-comm-expr.html"
:<<!
根据用户输入的一句话来定义数组
并遍历数组元素
!
echo "输入字符串(以空格分开):"
read str
i=0
for word in $str; do
    i=`expr $i + 1`
    eval a$i="$word"
    eval echo "数组的第 $i 个元素为: \$a$i"
done

基本运算符

# 算术运算符
	# 原生 bash 不支持简章的数学运算,但可以通过其它命令来实现(如 awk, expr)
val=`expr 2 + 2` # 表达式和运算符之间要有空格(2+2 是不对的)
echo $val
# + - * % / = == !=
`expr $a + $b`
`expr $a - $b`
`expr $a \* $b` # 需要转义 *, $(($a * b)) 不需要转义 *
`expr $a / $b`
`expr $a % $b`
val=$(expr 10 +20) # 推荐用 $() 代替 ``
val=$[ `expr 10 + 30` ]
val=$[ 10 + 20]

a=$b
[ $a == $b ] # 写成 [$a==$b] 是错的
[ $a != $b ]

if [ $a == $b ]
then
 	echo "a is equal to b"
else 
	echo "a is not equal to b"
fi

# 关系运算符(只支持数字,不支持字符串,除非字符串的值是数字)
[ $a -eq $b ]
[ $a -ne $b ]
[ $a -gt $b ]
[ $a -lt $b ]
[ $a -ge $b ]
[ $a -le $b ]


# 布尔运算符
[ !false ]
[ $a -lt 20 -o $b -gt 100 ]
[ $a -lt 20 -a $b -gt 100 ]

# 逻辑运算符
[[ $a -lt 100 && $b -gt 100 ]]
[[ $a -lt 100 || $b -gt 100 ]]

# 字符串运算符
[ $a = $b ]
[ $a != $b ]
[ -z $a ] # 检查字符串是否为 0
[ -n "$a"] # 检查字符串是否不为 0
[ $a ] # 检查字符串是否不为空

# 文件测试运算符
[ -b $file ] # 是否为块设备文件
[ -c $file ] # 是否为字符设备文件
[ -d $file ] # 是否为目录
[ -f $file ] # 是否为普通文件(不是目录、设备文件)
[ -g $file ] # 是否设置了 SGID 位
[ -k $file ] # 是否设置了粘着位(Sticky Bit)
[ -p $file ] # 是否为有名管道
[ -u $file ] # 是否设置了 SUID 位
[ -r $file ] # 文件是否可读
[ -w $file ] # 文件是否可写
[ -x $file ] # 文件是否可执行
[ -s $file ] # 文件是否不为空(文件大小是否大于 0)
[ -e $file ] # 文件或目录是否存在
[ -S $file ] # 文件是否为 socket
[ -L $file ] # 文件是否存在并且是一个符号链接

# [[]] 只是 [] 的扩充,能够支持 >, < 符号运算不需要转义,支持逻辑运算符 ||, &&,不再使用 -a, -o
# [[]]: >, <, &&, ||
# []: \>, \<, -a, -o
# 数值比较
# [ expression1 OP expression2 ]: -gt, -lt, -ge, -le, -eq, -ne
# ((expression1 OP expression2)): >, <, >=, <=, ==, !=
# >, <, ==, != 也可以进行字符串比较
# == 和 != 进行字符串比较时,可用 [ string1 OP string2 ] 或 [[ string1 OP string2 ]]
# > 和 < 进行字符串比较时,需要用 [ string1 \OP string2 ] 或 [[ string1 OP string2 ]]

echo 命令

echo It is a test
echo 'It is a test'
echo "It is a test"
echo "\"It is a test\""

echo -e "OK! \n" # -e 开启转义
echo -e "OK! \c" # \c 不换行
echo "It is a test"
# OK! It is a test

echo 'It is a test' > myfile
echo '$name\"' # $name\"

echo `date` # Wed Jan 29 14:28:47 2020

read 命令

read -p '请输入一段文字:' -n 6 -t 5 -s password
echo -e "\npassword is ${password}"

# -p 输入提示文字
# -n 输入字符长度限制(-n 6 达到 6 位自动结束)
# -t 输入限时
# -s 隐藏输入内容

重定向

> 重定向输出到某个位置,替换原有文件的所有内容
>> 重定向追加到某个位置,在原有文件的末尾添加内容
< 重定向输入某个位置文件

2> 重定向错误输出
2>> 重定向错误追加输出到文件末尾
&> 混合输出错误的和正确的都输出

printf 命令

# 语法:
# printf format-string [arguments ...]

# 不会像 echo 自动添加换行符
# 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等

printf "Hello, Shell\n"


printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234 
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543 
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876 
姓名     性别   体重kg
郭靖     男      66.12
杨过     男      48.65
郭芙     女      47.99

# %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来
# %-4.2f 指格式化为小数,其中.2指保留2位小数


# format-string为双引号
printf "%d %s\n" 1 "abc"

# 单引号与双引号效果一样 
printf '%d %s\n' 1 "abc" 

# 没有引号也可以输出
printf %s abcdef

# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def

printf "%s\n" abc def

printf "%s %s %s\n" a b c d e f g h i j

# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"

# 输出结果
1 abc
1 abc
abcdefabcdefabc
def
a b c
d e f
g h i
j  
 and 0


# \a 警告字符,通常为ASCII的BEL字符
# \b 后退
# \c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
# \f 换页(formfeed)
# \n 换行
# \r 回车
# \t 水平制符
# \v 垂直制表符
# \\ 一个字面上的反斜杠字符
# \ddd 表示 1 到 3 位数八进制值的字符,仅在格式字符串中有效
# \0ddd 表示 1 到 3 位的八进制值字符

$ printf "a string, no processing:<%s>\n" "A\nB"
a string, no processing:<A\nB>

$ printf "a string, no processing:<%b>\n" "A\nB"
a string, no processing:<A
B>

$ printf "www.runoob.com \a"
www.runoob.com $                  #不换行


:<<!
%d %s %c %f 格式替代符详解:

d: Decimal 十进制整数 -- 对应位置参数必须是十进制整数,否则报错!

s: String 字符串 -- 对应位置参数必须是字符串或者字符型,否则报错!

c: Char 字符 -- 对应位置参数必须是字符串或者字符型,否则报错!

f: Float 浮点 -- 对应位置参数必须是数字型,否则报错!

如:其中最后一个参数是 "def",%c 自动截取字符串的第一个字符作为结果输出。
!

$  printf "%d %s %c\n" 1 "abc" "def"
1 abc d

%b: 字符串--相对应的参数被视为含有要被处理的转义序列之字符串
$ printf "a string, no processing:<%b>\n" "A\nB"
a string, no processing:<A
B>
# 无论时在格式字符串内还是在使用 %b 所打印的参数字符串里,大部分的转义序列都是被相同对待。
# 无论如何,\c 与 \0ddd 只有搭配 %b 使用才有效,而 \ddd 只有在格式字符串里才会被解释

%f 格式化浮点数默认支持 6 位小数:
$ printf "%d %s %c %f\n" 10 "abc" "def" "3.1415926"
10 abc d 3.141593          # 格式化浮点数默认支持小数点后六位,后面多出的四舍五入

test 命令

# 用于检查某个条件是否成立(可以进行数值、字符和文件三个方面的测试)

# 数值测试
# -eq
# -ne
# -gt
# -ge
# -lt
# -le

num1=100
num2=100
if test $[num1] -eq $[num2]
then
	echo '两个数相等!'
else
	echo '两个数不相等!'
fi

a=5
b=6
result=$[a+b] # 注意等号两边不能有空格

# 字符串测试
# =
# !=
# -z 字符串    字符串长度是否为 0
# -n 字符串    字符串长度是否不为 0

str1="ru1noob"
str2="runnob"
if test $str1 = $str2
then
	echo '两个字符串相等'
else
	echo '两个字符串不相等'
fi

# 文件测试
# -e $file 文件存在则为真
# -r $file 文件存在且可读则为真
# -w $file 文件存在且可写则为真
# -x $file 文件存在且可执行则为真
# -s $file 文件存在且至少有一个字符则为真
# -d $file 文件存在且为目录则为真
# -f $file 文件存在且为普通文件则为真
# -c $file 文件存在且为字符型特殊文件则为真
# -b $file 文件存在且为块特殊文件则为真
# -O $file 判断对象是否存在且属于当前用户
# -G $file 判断对象是否存在且属于当前用户组
# [ "/data/file1" -nt "/data/file2" ] 判断 file1 是否比 file2 新
# [ "/data/file1" -ot "/data/file2" ] 判断 file1 是否比 file2 旧

if test -e /bin/bash
then
	echo '文件已存在!'
else
	echo '文件不存在!'
fi

# 逻辑操作符
# ! -a -o,优先级依次降低

if test -e ./notFile -o -e /bin/bash
then
	echo '至少有一个文件存在'
else
	echo '两个文件都不存在'
fi


result=$[a + b]
result=`expr $a + $b`



if [ ! -d "/data/" ];then
  mkdir /data
  else
  echo "文件夹已经存在"
fi
if [ ! -f "/data/filename" ];then
  echo "文件不存在"
  else
  rm -f /data/filename
fi
if [ -d "/data/" ];then
  echo "文件夹存在"
  else
  echo "文件夹不存在"
fi
if [ -f "/data/filename" ];then
  echo "文件存在"
  else
  echo "文件不存在"
fi

流程控制

条件分支

# 流程控制执行语句不可为空

if condition
then
	command1
	command2
	...
	commandN
fi

# 写成一行(适用于终端命令)
if [ $(ps -ef | grep -c "ssh") -gt 1 ] ; then echo "true";fi


if condition
then
	command1
	command2
	...
	commandN
else
	command
fi



if condition
	command1
then
elif condition2
then
	command2
else
	commandN
fi


a=10
b=20
if [ $a == $b ]
then
	echo 'a 等于 b'
elif [ $a -gt $b ]
then
	echo 'a 大于 b'
elif [ $a -lt $b ]
then
	echo 'a 小于 b'
else
	echo '没有符合的条件'
fi


num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
    echo '两个数字相等!'
else
    echo '两个数字不相等!'
fi

循环控制

for var in item1 item2 ... itemN
do
	command1
	command2
	...
	commandN
done

# 写成一行
for var in item1 item2 ... itemN; do command1; command2 ... commandN; done;

# for循环即执行一次所有命令,使用变量名获取列表中的当前取值
# in列表是可选的,如果不用它,for循环使用命令行的位置参数

for loop in 1 2 3 4 5
do
    echo "The value is: $loop"
done

for str in 'This is a string'
do
    echo $str
done


while condition
do
    command
done

int=1
while(( $int<=5 ))
do
    echo $int
    let "int++"
done


echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
    echo "是的!$FILM 是一个好网站"
done

# while循环可用于读取键盘信息
# while循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件

# 无限循环
while :
do
    command
done

while true
do
    command
done

for (( ; ; ))

# 一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用
# condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环
until condition
do
    command
done

a=0

until [ ! $a -lt 10 ]
do
   echo $a
   a=`expr $a + 1`
done


# 取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
case 值 in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac


echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
    1)  echo '你选择了 1'
    ;;
    2)  echo '你选择了 2'
    ;;
    3)  echo '你选择了 3'
    ;;
    4)  echo '你选择了 4'
    ;;
    *)  echo '你没有输入 1 到 4 之间的数字'
    ;;
esac


# 跳出循环
# break命令允许跳出所有循环(终止执行后面的所有循环)。
while :
do
    echo -n "输入 1 到 5 之间的数字:"
    read aNum
    case $aNum in
        1|2|3|4|5) echo "你输入的数字为 $aNum!"
        ;;
        *) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
            break
        ;;
    esac
done

# continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
while :
do
    echo -n "输入 1 到 5 之间的数字: "
    read aNum
    case $aNum in
        1|2|3|4|5) echo "你输入的数字为 $aNum!"
        ;;
        *) echo "你输入的数字不是 1 到 5 之间的!"
            continue
            echo "游戏结束"
        ;;
    esac
done



case 值 in
模式1)
    command1
    command2
    command3
    ;;
模式2)
    command1
    command2
    command3
    ;;
*)
    command1
    command2
    command3
    ;;
esac

site="runoob"

case "$site" in
   "runoob") echo "菜鸟教程"
   ;;
   "google") echo "Google 搜索"
   ;;
   "taobao") echo "淘宝网"
   ;;
esac

for((assignment;condition:next));do
    command_1;
    command_2;
    commond_..;
done;
for((i=1;i<=5;i++));do
    echo "这是第 $i 次调用";
done;
# 与 C 中相似,赋值和下一步执行可以放到代码之前循环语句之中执行,这里要注意一点:如果要在循环体中进行 for 中的 next 操作,记得变量要加 $,不然程序会变成死循环

函数

[ function ] funname [()]
{
    action;
    [return int;]
}
# 以带function fun() 定义,也可以直接fun() 定义,不带任何参数
# 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

demoFun(){
    echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"




funWithReturn(){
    echo "这个函数会对输入的两个数字进行相加运算..."
    echo "输入第一个数字: "
    read aNum
    echo "输入第二个数字: "
    read anotherNum
    echo "两个数字分别为 $aNum 和 $anotherNum !"
    return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"
# 函数返回值在调用该函数后通过 $? 来获得。
# $? 仅对其上一条指令负责,一旦函数返回后其返回值没有立即保存入参数,那么其返回值将不再能通过 $? 获得


function demoFun1(){
    echo "这是我的第一个 shell 函数!"
    return `expr 1 + 1`
}

demoFun1
echo $? # 2
echo $? # 0

# 注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可

# 函数参数
# 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值
funWithParam(){
    echo "第一个参数为 $1 !"
    echo "第二个参数为 $2 !"
    echo "第十个参数为 $10 !"
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
# 注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数

# 函数与命令的执行结果可以作为条件语句使用。要注意的是,和 C 语言不同,shell 语言中 0 代表 true,0 以外的值代表 false

#!/bin/bash

echo "Hello World !" | grep -e Hello
echo $?
echo "Hello World !" | grep -e Bye
echo $?
if echo "Hello World !" | grep -e Hello
then
    echo true
else
    echo false
fi

if echo "Hello World !" | grep -e Bye
then
    echo true
else
    echo false
fi

function demoFun1(){
    return 0
}

function demoFun2(){
    return 12
}

if demoFun1
then
    echo true
else
    echo false
fi

if demoFun2
then
    echo ture
else
    echo false
fi
其执行结果如下:

Hello World !
0
1
Hello World !
true
false
true
false

# grep 是从给定字符串中寻找匹配内容的命令。首先看出如果找到了匹配的内容,会打印匹配部分且得到的返回值 $? 为 0,如果找不到,则返回值 $? 为 1。
# 接下来分别将这两次执行的 grep 命令当作条件语句交给 if 判断,得出返回值 $? 为 0,即执行成功时,条件语句为 true,当返回值 $? 为 1,即执行失败时,条件语句为 false。

# 之后再用函数的 return 值作为测试,其中 demoFun1 返回值为 0,demoFun2 返回值选择了任意一个和 0 不同的整数,这里为 12。

# 将函数作为条件语句交给 if 判断,得出返回值为 0 时,依然为 true,而返回值只要不是 0,条件语句都判断为 false。

输入/输出重定向

command > file	将输出重定向到 file
command < file	将输入重定向到 file
command >> file	将输出以追加的方式重定向到 file
n > file 将文件描述符为 n 的文件重定向到 file
n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file
n >& m 将输出文件 m 和 n 合并
n <& m 将输入文件 m 和 n 合并
<< tag	将开始标记 tag 和结束标记 tag 之间的内容作为输入

# 需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

$ who > users
$ cat users
_mbsetupuser console  Oct 31 17:35 
tianqixin    console  Oct 31 17:35 
tianqixin    ttys000  Dec  1 11:33 

$ echo "菜鸟教程:www.runoob.com" > users
$ cat users
菜鸟教程:www.runoob.com
$ echo "菜鸟教程:www.runoob.com" >> users
$ cat users
菜鸟教程:www.runoob.com
菜鸟教程:www.runoob.com

$ wc -l users
2 users

$  wc -l < users
2 

command1 < infile > outfile
# 同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中

一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file

$ command 2 > file # 如果希望 stderr 重定向到 file,可以这样写
$ command 2 >> file

$ command > file 2>&1 # 如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写
$ command >> file 2>&1

command < file1 >file2 # command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2


# Here Document
# Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序
# 它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command
command << delimiter
    document
delimiter
# 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
# 开始的delimiter前后的空格会被忽略掉。

$ wc -l << EOF
    欢迎来到
    菜鸟教程
    www.runoob.com
EOF
3          # 输出结果为 3 行

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

cat << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF

欢迎来到
菜鸟教程
www.runoob.com

# 如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null
command > /dev/null
# /dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果


command > /dev/null 2>&1 # 如果希望屏蔽 stdout 和 stderr,可以这样写
# 放在>后面的&,表示重定向的目标不是一个文件,而是一个文件描述符

换言之 2>1 代表将stderr重定向到当前路径下文件名为1的regular file中,而2>&1代表将stderr重定向到文件描述符为1的文件(即/dev/stdout)中,这个文件就是stdout在file system中的映射

而&>file是一种特殊的用法,也可以写成>&file,二者的意思完全相同
此处&>或者>&视作整体,分开没有单独的含义



# 顺序问题
find /etc -name .bashrc > list 2>&1
# 我想问为什么不能调下顺序,比如这样
find /etc -name .bashrc 2>&1 > list

xxx > list 2>&1

先将要输出到stdout的内容重定向到文件,此时文件list就是这个程序的stdout,再将stderr重定向到stdout,也就是文件list

xxx 2>&1 > list

先将要输出到stderr的内容重定向到stdout,此时会产生一个stdout的拷贝,作为程序的stderr,而程序原本要输出到stdout的内容,依然是对接在stdout原身上的,因此第二步重定向stdout,对stdout的拷贝不产生任何影响

$ find /etc -names "*.txt"  >list 2>&1
从左往右执行,执行到 >list,此时的 stdout 为 list;而执行到 2>&1,表示 stderr 重定向到 stdout,这里也就是 list 文件。

因为 [ find /etc -names "*.txt" ] 这条命令是错误的( -names 应该是 -name)。

本来要输出到终端屏幕的错误信息:

find: unknown predicate `-names`
被重定向到了 stdout 也就是 list 文件中,所以屏幕不会出现错误信息,而是打印到了 list 文件中。
cat list 可以查看到 find: unknown predicate `-names' 就在里面。


直接在 FreeBSD 或者 csh 中使用 command > file 2>&1 时候会得到这个错误:Ambiguous output redirect。

出错的原因在于 FreeBSD 默认使用 csh,在 csh 中如果想把标准输出和错误输出同时重定向到一个文件,需要用下面命令 command >& file

文件包含

# 和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件

# 语法
. filename   # 注意点号(.)和文件名中间有一空格
或
source filename

# test1.sh
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

url="http://www.runoob.com"

# test2.sh
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

#使用 . 号来引用test1.sh 文件
. ./test1.sh

# 或者使用以下包含文件代码
# source ./test1.sh

echo "菜鸟教程官网地址:$url"

$ chmod +x test2.sh 
$ ./test2.sh 
菜鸟教程官网地址:http://www.runoob.com

# 注:被包含的文件 test1.sh 不需要可执行权限。

参考

  • https://www.runoob.com/linux/linux-shell.html

记录

  • 2020/01/29 18:08 创建
相关标签: 开发 运维