Linux Shell
Linux Shell基础
shell是名词,具体实现了shell的软件成为bash,其他编程语言
Shell的作用:
- 解释执行用户输入的命令或程序。
- 用户输入一条命令,Shell就解释一条。
- 键盘输入命令,Linxu给与响应的方式成为交互式。
Shell是一块包着系统核心的壳,处于操作系统的最外层,与用户直接对话,把用户的输入,解释给操作系统,然后处理操作系统返回的结果,输出到屏幕上与用户看到结果。
Shell脚本比较适合处理纯文本类型的数据,比如日志,配置文件,文本,网页文件,大多数都是纯文本类型的,因此shell可以方便的进行文本处理
什么是Shell脚本
当命令或者程序语句写在文件当中,我们执行文件,读取其中的代码,这程序文件称为shell脚本。
在shell脚本里定义多条Linux命令以及循环控制语句,然后将这些Linux命令一次性执行完毕,执行脚本文件的方式称为,非交互方式。
Windwos中存在“.bat”批处理脚本
Linux中常用“.sh”脚本文件
shell脚本规则
在Linux系统中,shell脚本(bash shell 程序)通常使用vim进行编辑,由Linux 命令,bash shell指令,逻辑控制语句核注释信息组成。
shebang
计算机程序中,shebang指的是出现在文本文件的第一行前两个字符#!
在Unix系统中,程序会分析shebang后面的内容,作为解释器的指令,例如
- 以
#!/bin/sh开头的文件,程序在执行时会使用/bin/sh,也就是bash解释器 - 以
#!/usr/bin/python开头的文件,代表指定python解释器去执行 - 以
#!/usr/bin/env解释器名称,是一种在不同平台上都正确找到解释器的办法
注意事项
- 如果脚本未指定
shebang,脚本执行的时候,默认用当前shell去解释脚本,即$HELL - 如果
shebang指定了可执行的解释器,如/bin/bash /usr/bin/python,脚本在执行时,文件名会作为参数传递给解释器 - 如果
#!指定的解释器程序没有可执行的权限,则会报错“bad interpreter:Permission denied”. - 如果
#!指定解释程序不是一个可执行文件,那么指定的解释器程序会被忽略,转而交给当前的$SHELL去执行这个脚本。 - 如果
#!指定的解释程序不存在,那么会报错“bad interpreter:No such file or directory” #!之后的解释程序,需要写其绝对路径(如:#!/bin/bash),他是不会自动到$PATH中寻找解释器的- 如果你使用
“bash test.sh”这样的命令来执行脚本,那么#!这一行会被忽略掉,解释器使用命令中显式指定的bash
脚本注释,脚本开发规范
# shebang指定解释器
#! /bin/bash
# 注释内容
# 这是为们的第一个shell脚本
# 打印内容
echo "我是shell个脚本"
执行Shell脚本的方式
直接输入shell脚本名 系统会去pase 文件内寻找执行
./shell脚本名 系统在当前文件架下寻找执行(需要运行权限, chmod +x shell文件名)
/bin/bash shell脚本名 指定解释器执行文件
source shell脚本名
. shell脚本名
bash < shell脚本名
脚本语言
shell脚本语言属于一种弱类型语言无需声明变量类型直接定义使用
强类型语言,必须先声明变量类型,之后再赋予同类型的值
ubuntu系统中支持的shell情况,有如下种类
$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/usr/bin/sh
/bin/dash
/usr/bin/dash
默认的sh解释器
$ ll /usr/bin/sh
lrwxrwxrwx 1 root root 4 Sep 25 09:24 /usr/bin/sh -> dash*
Shell的优势
Linux底层命令都支持shell语句,结合三剑客进行更高级的应用
擅长脚本开发,软件停启,监控报警,日志分析
#Linux默认shell
$ echo $SHELL
/bin/bash
bash基础特性
bash是什么
- bash是一个命令处理器,运行在文本窗口中,并能执行用户直接输入的命令
- bash还能从文件中读取Linux命令,称为脚本
- bash支持通配符,管道,命令替换,条件判断等逻辑控制语句
bash特性汇总
-
文件路径tab键补全
-
命令补全
-
快捷键
-
通配符
-
命令历史
- shell会保留会话其中用户提交执行的命令
$ history # 查看命令历史 -c:清空内存中命令历史, -r:从文件中恢复历史命令, 数字:显示最近n条命令 history 10
ls
...
history
$ echo $HISTSIZE # 可以修改保存的命令历史个数
1000
$ echo $HISTFILE # 存放历史命令的文件,用户退出登陆后,持久化命令个数
/home/si/.bash_history
$ ls -a ~/.bash_history
/home/si/.bash_history
$ ! 1960 # 感叹号加历史id执行历史命令
$ !! # 感叹号+感叹号执行上一次命令
- 命令别名
- 命令行展开
变量含义
数字字母下划线
$ name=123
$ echo ${name}
123
$ pstree # 查看进程树
Shell变量
作用域
- 本地变量,只针对当前shell生效
- 环境变量,全局变量,对所有的shell生效
- 局部变量,针对在shell函数或者shell脚本中定义的
位置参数变量
用于shell脚本中传递参数
特殊变量
shell内置的特殊功效的变量
$ $?
0:成功 1-255:错误码
自定义变量
变量赋值 varName=value
变量引用 ${varName} $varName
双引号可以进行替换,单引号为普通字符串
不同的执行方式,不同的Shell环境
每次调用bash/sh解释器执行脚本,都会开启一个子shell,因此不会保留当前的shell变量,通过pstree命令检查进程树
调用source是在当前的shell环境中加载脚本,因此保留变量
Shell变量面试题
父子shell
$ cat he.sh
var=`whoami`
$ echo $var
$ sh he.sh
$ echo $var
$ . he.sh
$ echo $var
si
环境变量设置
环境变量一般指的是用export内置命令导出的变量,用于定义shell的运行环境,保证shell命令的正确执行
shell通过环境变量确定登陆的用户名,PATH路径,文件系统等各种应用
环境变量可以在命令行中临时创建,但是用户退出shell终端,变量即丢失,如果要永久生效,则需要修改环境变量配置文件
- 用户个人配置文件
~/.bash_profile,~/.bashrc远程用户特有 - 全局配置文件
/etc/profile /etc/bashrc,且系统建议最好创建在/etc/profile.d/,而非直接修改主文件,修改全局配置文件,影响所有登陆系统的用户。
检查系统环境变量的命令
set 输出所有变量,包括全局变量和局部变量(脚本文件中的变量)
env 只显示全局变量
declare 输出所有的变量,同set
export 显示和设置环境变量值
unset 变量名,撤销删除变量或函数
readonly 设置变量为只读,shell结束变量失效
bash多命令执行
$ export | awk -F '[ :=]' '{print $3}'
------------
COLORTERM
CONDA_DEFAULT_ENV
...
环境变量初始化与加载顺序
- ssh登陆Linux后,系统启动一个bash shell,bash会读取若干个系统环境文件,检查环境变量设置
- /etc/profile:全局环境变量文件,为系统的每个用户设置环境信息,当用户第一次登陆时,该文件被执行,并从/etc/profile.d目录的配置文件中收集shell的设置
- 然后读取/etc/profile.d目录下的脚本有系统诸多脚本,也放入自定义需要登陆加载的脚本便于用于登陆后立即运行脚本
- 运行$HOME/.bash_profile(用户环境变量文件)
- 运行$HOME/.bashrc
- 运行/etc/bashrc
取出变量值
- 单引号:所见即所得
- 双引号:输出引号里所有内容,识别特殊符号,弱引用
- 无引号:连续的符号可以不加引号,有空格则有歧义,最好用双引号
- 反引号:引用命令执行结果,等于$()用法
特殊变量
shell的特殊变量,用在脚本,函数传递参数使用,有如下特殊的,位置参数变量
$0 获取shell脚本文件名,以及脚本路径
$n 获取shell脚本的第n个参数,n在1-9之间,如$1,$2,$9,大于9则需要写,¥{10},输入时参数空格隔开
$# 获取执行shell脚本后面的参数总个数
$* 获取shell脚本所有参数,不加引号等同于$@作用,加上引号"$*"作用是 接收所有参数为单个字符串,如"$1 $2..."
$@ 不加引号,效果同上,加引号,是接收所有参数为独立字符串,如"$1""$2"...空格保留
$! 获取上次后台执行的pid
$$ 获取当前执行的pid
$_ 获取上次传入的最后一个参数
实践
$ cat text_var.sh
#! /bin/bash
echo '特殊变量 $0 $1 $2'
echo $0 $1 $2
echo '------------'
echo '特殊变量 $#'
echo $#
echo '------------'
echo '特殊变量 $*'
echo $*
echo '------------'
echo '特殊变量 $@'
echo $@
$ bash text_vat.sh one two 180 180 180 180
特殊变量 $0 $1 $2
text_ver.sh one two
------------
特殊变量 $#
6
------------
特殊变量 $*
one two 180 180 180 180
------------
特殊变量 $@
one two 180 180 180 180
$ cat text_ver.sh
[ $# -ne 2 ] && {
echo "aaaa"
exit 119
}
echo ok
$ ./text_ver.sh
aaaa
$ ./text_ver.sh as as
ok
Shell子串
bash一些基础的内置命令
echo # 打印内容 默认自带换行 平替printf函数
------------
-n 不换行输出
-e 解析字符串中的特殊符号
\n 换行
\r 回车
\t 指标符
\b 退格
##################
eval # 一次执行多个命令
------------
$ eval ls;cat text_ver.sh
text_ver.sh
[ $# -ne 2 ] && {
echo "aaaa"
exit 119
}
echo ok
##################
exec # 不创建子进程,执行后续命令,且执行完毕后,自动exit
------------
$ sh
$ exec date
Sat Oct 21 10:25:46 PM CST 2023
##################
export # 显示和设置环境变量值
##################
read
##################
read
##################
unset name # 取消指定的变量
##################
time # 统计命令时长
------------
$ time ls
...
real 0m0.004s # 实际运行的时间
user 0m0.000s # 用户态执行的时间
sys 0m0.004s # 内核态执行的时间
##################
for number in {1..100} # for 循环语句
do
echo $number
down
------------
$ for num in {1..100};do echo $num ; done
##################
seq
------------
$ seq -s ":" 10
1:2:3:4:5:6:7:8:9:10
##################
shell子串的花式用法
${变量} 返回变量值
------------
$ name=wordname
$ echo ${name}
wordname
##################
${#变量} 返回变量长度,字符长度,速度最快
------------
$ name=wordname
$ echo ${#name} == $ echo $name | wc -L == $ expr length $name == $ echo "${name}" | awk '{print length($0)}'
8
##################
${变量:4} 从4索引开始返回
------------
$ name=wordname
echo ${name:4}
name
##################
${变量:4:1} 从4索引开始,输出1个字符长度
------------
$ name=wordname
$ echo ${name:4:2}
na
##################
${变量#word} 从变量开头开始删除最短匹配的word子串,支持模糊匹配
------------
$ name=abcABC123ABCabc
$ echo ${name#a*c}
ABC123ABCabc
##################
${变量##word} 从变量开头开始删除最长匹配的word子串,支持模糊匹配
------------
$ name=abcABC123ABCabc
$ echo ${name##a*c}
##################
${变量%word} 从变量结尾开始删除最短匹配的word子串,支持模糊匹配
------------
$ name=abcABC123ABCabc
$ echo ${name%a*c}
abcABC123ABC
##################
${变量%%word} 从变量结尾开始删除最长匹配的word子串,支持模糊匹配
------------
$ name=abcABC123ABCabc
$ echo ${name%%a*c}
wordname
##################
${变量/pattern/string} 从string代替第一个匹配的pattern,支持模糊匹配
------------
$ name=abcABC123ABCabc
$ echo ${name/a*c/xyz}
xyz
$ echo ${name/abc/xyz}
xyzABC123ABCabc
##################
${变量//pattern/string} 从string代替所有的pattern,支持模糊匹配
------------
$ name=abcABC123ABCabc
~$ echo ${name//abc/xyz}
xyzABC123ABCxyz
##################
result=${变量:-word} 变量为空返回word字符串赋值给result变量
------------
$ result=${sy:-heihei}
$ echo $result; echo $sy
heihei
##################
result=${变量:=word} 变量为空word字符串替代变量值,且返回值
------------
$ result=${sy:=heihei}
echo $result $sy
heihei heihei
##################
result=${变量:?word} 变量为空word当作stderr输出,否则输出变量值,用于设置变量为空时返回错误信息
------------
$ result=${sy:?heihei}
bash: sy: heihei
$ echo $result $sy
$ sy=123
$ result=${sy:?heihei}
$ echo $result $sy
123 123
##################
result=${变量:+word} 变量为空,什么都不作,否则返回word
------------
$ result=${sy:+heihei}
$ echo $result $sy
$ sy=123
$ result=${sy:+heihei}
$ echo $result $sy
heihei 123
实践
$ touch sy_{1..5}_finished.jpg
$ touch sy_{1..5}_finished.png
$ ls
sy_1_finished.jpg sy_2_finished.png sy_4_finished.jpg sy_5_finished.png
sy_1_finished.png sy_3_finished.jpg sy_4_finished.png
sy_2_finished.jpg sy_3_finished.png sy_5_finished.jpg
$ for n in `ls *_fin*.jpg`;do mv $n ${n//_finished/};done
$ ls *.jpg
sy_1.jpg sy_2.jpg sy_3.jpg sy_4.jpg sy_5.jpg
进程列表
可以实现并发执行,提高执行效率
shell的进程列表理念,需要使用小括号(),如下执行,这样就称为进程列表
$ (cd ~;pwd;cd /tmp/;pwd;ls)
BASH_SUBSHELL变量,取值,如果是0则为父shell
$ (cd ~;pwd;cd /tmp/;pwd;ls;echo $BASH_SUBSHELL)
------------
...
1
子进程嵌套
$ (cd ~;pwd;(cd /tmp/;pwd;echo $BASH_SUBSHELL);echo $BASH_SUBSHELL)
------------
...
2 # 第2层子shell
1 # 第1层子shell
内置命令,外置命令
-
内置命令:在系统启动时就加入内存,常驻内存,执行效率更高,但是占资源
- 不会产生子进程,内置命令和shell是为一体的,是shell的一部分,不需要单独去读取某个文件,系统启动后就执行在内存中。
compgen -b # 查看shell内置命令
-
外置命令:系统需要从硬盘中读取程序文件,再读入内存加载
-
父进程衍生出子进程,子进程执行外置命令。
shell脚本开发
为了方便执行多行命令
编写Shebang指定解释器
执行脚本的方式
直接输入shell脚本名 系统会去pase 文件内寻找执行
./shell脚本名 系统在当前文件架下寻找执行(需要运行权限, chmod +x shell文件名)
/bin/bash shell脚本名 指定解释器执行文件
source shell脚本名
. shell脚本名
bash < shell脚本名
echo 在liunx下进行格式化打印
echo "${date}\n`date`\n$(date)"
------------
$var 引用变量替换
${} 引用变量替换
`` 执行命令并获取结果
$() 在子解释器中执行并获取结果
算术运算
(()) 用于整数计算
------------
((i=i+1))
i=$((i+1))
((8>7&&5==5))
echo $((2+1))
##################
let 用于整数计算,等同于(())但是没有它效率高
------------
firstnum=1
$ let firstnum=$firstnum+1
$ echo $firstnum
2
##################
expr 用于整数计算,还有其他功能
------------
必须是以传入参赛的形式进行
$ expr 5 \+ 3 # 数学符号需要转义
$ expr --help
$ expr sy.png ":" ".*\.png"
7
``````
#! /bin/bash
if expr "$1" ":" ".*\.jpg" &> /dev/null
then
echo "这是jpg结尾的"
else
echo "这不是jpg结尾的"
fi
``````
##################
bc Linux下的计算器(适合整数和小数运算)
------------
$ bc # 进入交互式计算器
$ num=5
$ result=`echo $num*5`
$ echo $result
5*5
$ result=`echo $num*5|bc`
$ echo $result
25
$ echo {1..100} |tr " " "+"
1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100
$ echo {1..100} |tr " " "+" |bc
5050
$ seq -s "+" 100
1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100
$ seq -s "+" 100 |bc
5050
##################
$[] 用于整数计算
------------
$ res=$[5+4]
##################
awk awk即可用于整数计算,也可小数计算
------------
$ echo "3.2 2.2" |awk '{print ($1*$2))}'
##################
declare 定义变量和属性值,-i参数可以用于定义整形变量,作运算
条件测试
test <条件表达式>
[ 条件表达式 ] # 和test是一样的
------------
$ test -e filename
$ [ -e filename ]
-e 文件名是否 存在
-f 文件名是否 是文件
-d 文件名是否 是目录
-s 文件名是否 是非空文件
-b 文件名是否 为一个block device装置
-c 文件名是否 为一个character device装置
-S 文件名是否 为一个Socket文件
-p 文件名是否 为一个FIFO(pipe)文件
-L 文件名是否 为一个连结档
-r 文件名是否 有可读属性
-w 文件名是否 有可写属性
-x 文件名是否 有可执行属性
-u 文件名是否 有SUID属性
-g 文件名是否 有SGID属性
-k 文件名是否 有Sticky bit属性
$ test file1 -nt file2
$ [ file1 -nt file2 ]
-nt 1是否比2新
-ot 1是否比2旧
-ef 1是否和2是同一个文件,主要用意是判断两个文件是否指向一个inode
$ test n1 -eq n2
$ [ n1 -eq n2 ]
-eq 两数值相等
-ne 两数值不相等
-gt n1大于n2
-lt n1小于n2
-ge n1大于等于n2
-le n1小于等于n2
$ test -z string # 判定字符串是否为空,空为true
[ -z string ]
$ test -n string # 判定字符串是否为空,非空为true
[ -n string ]
$ test str1 = str2 # 判定字符串是否相等,相等为true
[ str1 = str2 ]
$ test str1 != str2 # 判定字符串是否相等,不等为true
[ str1 != str2 ]
$ test -r filename -a -x filename
[ -r filename -a -x filename ]
-a 俩个情况同时成立
-o 两个情况一个成立
! 两个情况刚好相反
##################
[[]] # 是[]的加强,支持直接使用数学符号,支持正则
------------
##################
(())
------------
##################
定义函数
shell脚本自上而下执行加载
执行shell函数直接写函数名即可,无需添加其他内容,函数必须先定义,再执行.
函数体内定义的变量为局部变量
函数体内需要加return语句作用是退出函数,且赋予赋予返回值给调用该函数的程序,也就是shell加本(在shell脚本中,定义,使用函数,shell脚本执行结束后,通过$?获取其return的返回值)
return语句和返回值不同,(return只能写在函数中,exit是shell内置命令,用于退出shell环境)
return是结束函数的执行,返回一个退出值/返回值
exit是结束shell环境,返回一个退出值/返回值给当前的shell
函数如果单独写在文件中,需要用source读取(函数的另一种用法,写入文件中,用于加载)
函数内,使用local关键字定义局部变量。
##########
同文件内定义并调用函数
$ cat test.sh
#! /bin/bash
# 脚本开发
# 函数的作用,就算把写好的功能代码进行打包,封装成一个函数名,需要使用该功能直接调用函数名即可。
# 函数的名字
print_usage(){
echo "你好"
}
# 执行该函数
print_usage
$ /bin/bash test.sh
你好
##########
其他文件引用执行函数
$ vim my_foc.sh
sa(){
echo "我是函数sa 我被调用了"
}
# 方法一
$ source */myfoc.sh # 加载定义的函数到变量中
# 方法二
$ vim func1.sh
./*/my_foc.sh # 加载函数到文件中
sa
检查网站是否存活函数
#! /bin/bash
# 监测网站存活
CheckUrl(){
# 超时等待时间
timeout=5
# 访问成功还是失败,相当于计数器
fails=0
success=0
# 循环执行命令
while true
do
wget --timeout=${timeout} --tries=1 http://www.baidu.com -q -o ~/桌面/testlog.log
if [ $? -ne 0 ] # 不等于
then
let fails=fails+1
else
let success+=1
fi
if [ $success -ge 1 ] # 大于等于
then
echo "运行中"
exit 0
fi
if [ $fails -ge 2 ];then
echo "停止中"
exit 2
fi
done
}
CheckUrl
实战训练
定时监测内存
# 1.获取当前内存,获取available数据, 第二行最后一个字段
FreeMem=`free -m |awk 'NR==2 {print $NF}'`
CHARS="内存剩余 $FreeMem"
# 2.判断小于多少
if [ "$FreeMem" -lt "100" ]
then
echo $CHARS|tee /home/si/桌面/messages.txt
# 3.使用mail服务发送邮件
mail -s "`date +%F-%T`$CHARS" elm_security@163.com < /home/si/桌面/messages.txt
fi
# 4.加入定时任务
crontab -e # 编辑定时任务
C程序调用shell脚本
共有三种方法 :system()、popen()、exec系列函数
1. system()
不用你自己去产生进程,它已经封装了,直接加入自己的命令
例如:system("ls -l /etc")
2. exec()系列函数
需要自己 fork 进程,然后exec 自己的命令
#include <unistd.h>
int execl(const char *pathname, const char *arg0, ... /* (char *)0 */ );
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, .../* (char *)0, char *const envp[] */ );
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ... /* (char *)0 */ );
int execvp(const char *filename, char *const argv[]);
int fexecve(int fd, char *const argv[], char *const envp[]);
// 如果filename中包含/,则将其视为路径名,否则就按PATH环境变量,在它所指的各目录搜寻可执行文件.
返回值:如果执行成功将不返回,否则返回-1,失败代码存储在errno中.
前4个函数取路径名作为参数,后两个是取文件名作为参数,最后一个是以一个文件描述符作为参数.
execl()函数用来执行参数path字符串所指向的程序,第二个及以后的参数代表执行文件时传递的参数列表,最后一个参数必须是空指针以标志参数列表的结束。
int execl(const char *path, const char *arg0, ... /* (char *)0 */ );
例如:
int execl("/bin/ls","ls","-l","/etc",(char *)0)
execv()函数函数用来执行参数path字符串所指向的程序,第二个为数组指针维护的程序参数列表,该数组的最后一个成员必须是空指针.
int execv(const char *path, char *const argv[]);
例如:
#include <unistd.h>
int main()
{
char *argv[] = {"ls", "-l", "/etc", (char *)0};
execv("/bin/ls", argv);
return 0;
}
execlp()函数会从PATH环境变量所指的目录中查找文件名为第一个参数指示的字符串,找到后执行该文件,第二个及以后的参数代表执行文件时传递的参数列表,最后一个参数必须是空指针.
int execlp(const char *filename, const char *arg0, ... /* (char *)0 */ );
execvp()函数会从PATH环境变量所指的目录中查找文件名为第一个参数指示的字符串,找到后执行该文件,第二个参数代表执行文件时传递的参数列表,最后一个成员必须是空指针.
int execvp(const char *file, char *const argv[]);
例如:
#include <unistd.h>
void main()
{
char *argv[] = {"ls","-l", "/home/kaiming",(char *)0};
execvp("ls",argv); #环境变量只有/bin,所以这里的第一个参数还要写“ls”
}
exec系列函数通常需要和fork函数一起使用:
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
- 在父进程中,fork返回新创建子进程的进程ID;
- 在子进程中,fork返回0;
- 如果出现错误,fork返回一个负值;
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
父进程
int main(){
pid_t pid;
pid = fork(); //pid =子进程的ID
if(pid<0){
printf("error fork:%m\n");
exit(-1);
}
else if(pid==0){
execl("/bin/ls","ls","-l","/etc",(char *)0);
}
else{
printf("parent process\n");
}
return 0;
}
//子进程
pid = fork(); //pid=0
if(pid<0){
printf("error fork:%m\n");
exit(-1);
}
else if(pid==0){
execl("/bin/ls","ls","-l","/etc",(char *)0);
}
else{
printf("parent process\n");
}
return 0;
}
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
通过创建一个管道,调用fork()产生一个子进程,执行一个shell以运行command命令。
type参数只能是"r"或者"w"中的一种,得到的返回值(标准I/O流)也具有和type相应的只读或只写类型。如果type是"r"则文件指针连接到command的标准输出;如果type是"w"则文件指针连接到command的标准输入。
command参数是一个指向以NULL结束的shell命令字符串的指针。这行命令将被传到/bin/sh并使用-c标志,shell将执行这个命令。
返回值:若成功则返回文件指针,否则返回NULL,差错原因存于errno中。返回值是个标准I/O流,必须由pclose来终止
popen()函数
FILE * popen(char *command, char *type)
函数fgets()从给出的文件流中读取[num - 1]个字符并且把它们转储到str(字符串)中. fgets()在到达行末时停止,在这种情况下,str(字符串)将会被一个新行符结束. 如果fgets()达到[num - 1]个字符或者遇到EOF, str(字符串)将会以null结束.fgets()成功时返回str(字符串),失败时返回NULL.
#include <stdlib.h>
#include <stdio.h>
#define BUF_SIZE 1024
char buf[BUF_SIZE];
int main(void)
{
FILE * p_file = NULL;
p_file = popen("ifconfig", "r"); #因为返回值是"标准I/O流",所以并不会在屏幕上显示结果,必须配合fgets()和fprintf()才能显示结果
if (!p_file) {
fprintf(stderr, "Erro to popen");
}
while(fgets(buf, BUF_SIZE, p_file)!=NULL){
fprintf(stdout, "%s", buf);
}
pclose(p_file);
return 0;
}
char *fgets( char *str, int num, FILE *stream );fprintf()函数根据指定的format(格式)(格式)发送信息(参数)到由stream(流)指定的文件. fprintf()只能和printf()一样工作. fprintf()的返回值是输出的字符数,发生错误时返回一个负值.
fprintf()函数根据指定的format(格式)(格式)发送信息(参数)到由stream(流)指定的文件. fprintf()只能和printf()一样工作. fprintf()的返回值是输出的字符数,发生错误时返回一个负值.
#include <stdlib.h>
#include <stdio.h>
#define BUF_SIZE 1024
char buf[BUF_SIZE];
int main(void)
{
FILE * p_file = NULL;
p_file = popen("ifconfig", "r"); #因为返回值是"标准I/O流",所以并不会在屏幕上显示结果,必须配合fgets()和fprintf()才能显示结果
if (!p_file) {
fprintf(stderr, "Erro to popen");
}
while(fgets(buf, BUF_SIZE, p_file)!=NULL){
fprintf(stdout, "%s", buf);
}
pclose(p_file);
return 0;
}
char *fgets( char *str, int num, FILE *stream );
fprintf()函数根据指定的format(格式)(格式)发送信息(参数)到由stream(流)指定的文件. fprintf()只能和printf()一样工作. fprintf()的返回值是输出的字符数,发生错误时返回一个负值.
int fprintf( FILE *stream, const char *format, ... );
C语言标准I/O库中的操作都是围绕流进行的。当我们打开或创建一个文件时,就会将一个流与该文件关联。在流式的I/O操作中,系统会为操作分配缓冲区,以减少对read、write的调用次数。

浙公网安备 33010602011771号