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 创建
上一篇: 记一次运维开发面试
下一篇: Oracle 锁表以及解锁