UNIX环境高级编程看了三章,遇到不少重定向等shell命令。本想到Linux时再讲,看来有必要提前了。之前有看过一本《嵌入式Linux软硬件开发详解》这本书里有简单介绍了一部分shell常用命令,就结合它来简单介绍下shell编程。毕竟没有详细看过shell相关的书籍,等以后看过了再详细讲吧。
首先,推荐一款 shell 在线工具
扩展学习:Shell 教程
相关书籍:Linux命令行与Shell脚本编程大全
常用快捷键:
Ctrl+C强制终止当前命令
Ctrl+L 清屏,相当于clear
Ctrl+U 删除或者剪切光标之前的所有命令,比退格方式更快捷
Ctrl+K 删除或者剪切光标之后的所有命令
Ctrl+Y 粘贴Ctrl+U或者Ctrl+K剪切的内容
Ctrl+R 实现搜索历史命令,先输入Ctrl+R,然后回车再输入需要搜索的历史命令
Ctrl+D 退出当前终端
Ctrl+Z 暂停命令并且放入后台,不能经常使用
Ctrl+S 暂停屏幕输出
Ctrl+Q 恢复屏幕输出
查看 /etc/passwd :
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
可以看到:root:x:0:0:root:/root:/bin/bash
所以,我所执行的是 bash shell
-t:输出“file”、“alias”或者“builtin”,分别表示给定的指令为“外部指令”、“命令别名”或者“内部指令”;
-p:如果给出的指令为外部指令,则显示其绝对路径;
-a:在环境变量“PATH”指定的路径中,显示给定指令的信息,包括命令别名。
# type ls
ls 是 `ls --color=auto' 的别名
# type -t ls
alias
# type -a ls
ls 是 `ls --color=auto' 的别名
ls 是 /bin/ls
可以看出,ls为命令别名
# type cd
cd 是 shell 内嵌
# type -t cd
builtin
# type -a cd
cd 是 shell 内嵌
可以看出,cd为内部命令
# type which
which 是 /usr/bin/which
# type -t which
file
# type -p which
/usr/bin/which
# type -a which
which 是 /usr/bin/which
which 是 /bin/which
可以看出,which为外部命令
通过,type 命令的用法,我们可以知道每个命令是否为 bash 内置命令。此外,使用 type 搜索后面的名称时,如果后接的名称不能以执行文件的状态找到,那么该名称不会显示。
简单显示:
# echo hello
hello
创建shell脚本:
gedit hello.sh
#!/bin/bash
echo "hello world!"
执行 ./hello.sh
bash: ./hello.sh: 权限不够
添加权限:chmod +x *.sh 或者 chmod 777 *.sh
# ./hello.sh
hello world!
\a 发出警告声;
\b 删除前一个字符;
\c 最后不加上换行符号;
\f 换行但光标仍旧停留在原来的位置;
\n 换行且光标移至行首;
\r 光标移至行首,但不换行;
\t 插入tab; \v 与\f相同;
\\ 插入\字符;
\nnn 插入nnn(八进制)所代表的ASCII字符;
# echo -e "\e[1;31mThis is red text\e[0m"
This is red text
# echo -e "\e[1;42mGreed Background\e[0m"
Greed Background
# export HELLO="hello"
# echo $HELLO
hello
# export
declare -x COLORTERM="gnome-terminal"
declare -x DBUS_SESSION_BUS_ADDRESS="unix:abstract=/tmp/dbus-Kss6b0aquA,guid=297ad74aed4e17b7f89f981d0000003c"
declare -x DEFAULTS_PATH="/usr/share/gconf/ubuntu-2d.default.path"
declare -x DESKTOP_SESSION="ubuntu-2d"
declare -x DISPLAY=":0"
declare -x GDMSESSION="ubuntu-2d"
declare -x GNOME_DESKTOP_SESSION_ID="this-is-deprecated"
declare -x GNOME_KEYRING_CONTROL="/tmp/keyring-qyAxFa"
declare -x GPG_AGENT_INFO="/tmp/keyring-qyAxFa/gpg:0:1"
declare -x GTK_IM_MODULE="ibus"
declare -x HOME="/root"
.....
# export HELLO="hello"
# readonly HELLO="hello"
# export HELLO="hello" world
bash: HELLO: 只读变量
# unset HELLO
bash: unset: HELLO: 无法反设定: 只读 variable
# readonly
declare -r BASHOPTS="checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath"
declare -ir BASHPID
declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="24" [3]="1" [4]="release" [5]="i686-pc-linux-gnu")'
declare -ir EUID="0"
declare -ir PPID="2590"
declare -r SHELLOPTS="braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor"
declare -ir UID="0"
创建shell脚本:
gedit hello.sh
#!/bin/bash
echo -e "Please enter: \c"
read x
echo "you enter: $x"
执行脚本:
# ./hello.sh
Please enter: hello world!
you enter: hello world!
再例如,终端输入密码时候,不让密码显示出来,可创建如下脚本:
方法一:
创建脚本:
#!/bin/bash
read -p "输入密码:" -s pwd
echo echo password read, is "$pwd"
执行脚本:
./hello.sh
输入密码:
password read, is 12345
方法二:
创建脚本:
#!/bin/bash
stty -echo
read -p "输入密码:" pwd
stty echo
echo
echo 输入完毕
执行脚本:
./hello.sh
输入密码:
输入完毕
注意: stty -echo
# env
LC_PAPER=en_US.UTF-8
LC_ADDRESS=en_US.UTF-8
SSH_AGENT_PID=1749
LC_MONETARY=en_US.UTF-8
GPG_AGENT_INFO=/tmp/keyring-qyAxFa/gpg:0:1
TERM=xterm
SHELL=/bin/bash
.....
# set
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=()
BASH_SOURCE=()
.....
# export HELLO="hello"
# unset HELLO
# echo $HELLO
#
-a 不要忽略二进制数据。
-A<显示列数> 除了显示符合范本样式的那一行之外,并显示该行之后的内容。
-b 在显示符合范本样式的那一行之外,并显示该行之前的内容。
-c 计算符合范本样式的列数。
-C<显示列数>或-<显示列数> 除了显示符合范本样式的那一列之外,并显示该列之前后的内容。
-d<进行动作> 当指定要查找的是目录而非文件时,必须使用这项参数,否则grep命令将回报信息并停止动作。
-e<范本样式> 指定字符串作为查找文件内容的范本样式。
-E 将范本样式为延伸的普通表示法来使用,意味着使用能使用扩展正则表达式。
-f<范本文件> 指定范本文件,其内容有一个或多个范本样式,让grep查找符合范本条件的文件内容,格式为每一列的范本样式。
-F 将范本样式视为固定字符串的列表。
-G 将范本样式视为普通的表示法来使用。
-h 在显示符合范本样式的那一列之前,不标示该列所属的文件名称。
-H 在显示符合范本样式的那一列之前,标示该列的文件名称。
-i 胡列字符大小写的差别。
-l 列出文件内容符合指定的范本样式的文件名称。
-L 列出文件内容不符合指定的范本样式的文件名称。
-n 在显示符合范本样式的那一列之前,标示出该列的编号。
-q 不显示任何信息。
-R/-r 此参数的效果和指定“-d recurse”参数相同。
-s 不显示错误信息。
-v 反转查找。
-w 只显示全字符合的列。
-x 只显示全列符合的列。
-y 此参数效果跟“-i”相同。
-o 只输出文件中匹配到的部分。
常用方法:
当前所有含 hello 的文件
# grep "hello" * -Rn
当前所有不含 hello 的文件
# grep "hello" * -vn
-c或--bytes或——chars:只显示Bytes数;
-l或——lines:只显示列数;
-w或——words:只显示字数。
实例:
# wc aio.h
246 967 7502 aio.h
分别是: 列数 字数 字节数 文件名
新建text.txt
# touch text.txt
将ps内容输出到 text.txt 文件中
# ps > text.txt
查看 text.txt 内容
# cat text.txt
PID TTY TIME CMD
2600 pts/0 00:00:00 bash
3231 pts/0 00:00:00 ps
如果再将 ls内容使用 ">" 输出到 text.txt 文件中
# ls > text.txt
再查看 text.txt内容,则覆盖掉了原来的内容
# cat text.txt
text.txt
如果再将 ps内容使用 ">>" 输出到 text.txt 文件中
# ps >> text.txt
再查看 text.txt内容,则附加到原来的内容的结尾
# cat text.txt
text.txt
PID TTY TIME CMD
2600 pts/0 00:00:00 bash
3236 pts/0 00:00:00 ps
hello没有这个文件,查看会出错
# cat hello
cat: hello: 没有那个文件或目录
将错误保存到 text.txt 文件中
# cat hello 2> text.txt
# cat text.txt
cat: hello: 没有那个文件或目录
查看脚本
# cat sh.sh
echo “you working directory is $(pwd)”
echo "the time is $(date)"
shell命令解析程序从脚本程序 sh.sh 中读取命令行并加以执行
# bash < sh.sh
“you working directory is /home/tarena/project/c_test”
the time is Wed Mar 22 10:07:45 CST 2017
ls 命令查看
# ls
sh.sh text.txt
可以可以指定查找脚本文件
# ls | grep *sh
sh.sh
#!/bin/bash
符号“#!”用来告诉系统它后面的参数是用来执行该文件的程序,在这个例子中使用 /bin/bash 来执行程序。
chmod +x [文件] 或者 chmod 777 [文件]
修改文件权限,之前也有讲可参看:C语言再学习 -- 修改linux文件权限查看脚本
# cat sh.sh
#!/bin/bash
echo $(ls)
echo $SHELL
执行脚本
# ./sh.sh
sh.sh text.txt
/bin/bash
#!/bin/bash
#program date
#show the date in this way
echo "Mr.$USER,Today is:"
#echo $(date)
echo $(date)
echo Wish you a lucky day !
# chmod 777 sh.sh
# ./sh.sh
Mr.root,Today is:
Wed Mar 22 10:47:33 CST 2017
Wish you a lucky day !
# here=beijing
此时系统便定义了 here 这个内容 为 beijing 的变量。查看变量内容我们可以在变量前面加上一个“$”符号,再用 echo 命令将其内容输出到终端上。
# here=beijing
# echo $here
beijing
这部分类似于环境变量,在此不多讲,参看:UNIX再学习 -- 环境变量# echo 1 2 3
1 2 3
分析:有 4 个位置参数
1 个命令名(echo) + 3 个参量 (1、2、3),$0 = ehco $1 = 1 $2 = 2 $3 = 3
#!/bin/bash
echo "one=$1"
echo "two=$2"
echo "three=$3"
echo "count=$#"
echo "all=$*"
echo "all=$@"
set A B C
echo "all=$*"
执行脚本:# ./sh.sh 1 2 3
one=1
two=2
three=3
count=3
all=1 2 3
all=1 2 3
all=A B C
#!/bin/bash
#if test -f "$1"
if [ -f "$1" ]
then echo "$1 is an ordinary file."
else echo "$1 is not ordinary file."
fi
执行脚本:# ./sh.sh hello.txt
hello.txt is not ordinary file.
if test #表达式为真
if test ! #表达式为假
test 表达式1 –a 表达式2 #两个表达式都为真
test 表达式1 –o 表达式2 #两个表达式有一个为真
test 表达式1 ! 表达式2 #条件求反
判断字符串:
test –n 字符串 #字符串的长度非零
test –z 字符串 #字符串的长度是否为零
test 字符串1=字符串2 #字符串是否相等,若相等返回true
test 字符串1!=字符串2 #字符串是否不等,若不等反悔false
判断整数:
test 整数1 -eq 整数2 #整数相等
test 整数1 -ge 整数2 #整数1大于等于整数2
test 整数1 -gt 整数2 #整数1大于整数2
test 整数1 -le 整数2 #整数1小于等于整数2
test 整数1 -lt 整数2 #整数1小于整数2
test 整数1 -ne 整数2 #整数1不等于整数2
判断文件:
test File1 –ef File2 两个文件是否为同一个文件,可用于硬连接。主要判断两个文件是否指向同一个inode。
test File1 –nt File2 判断文件1是否比文件2新
test File1 –ot File2 判断文件1比是否文件2旧
test –b file #文件是否块设备文件
test –c File #文件并且是字符设备文件
test –d File #文件并且是目录 test –e File #文件是否存在 (常用)
test –f File #文件是否为正规文件 (常用)
test –g File #文件是否是设置了组id
test –G File #文件属于的有效组ID
test –h File #文件是否是一个符号链接(同-L)
test –k File #文件是否设置了Sticky bit位
test –b File #文件存在并且是块设备文件
test –L File #文件是否是一个符号链接(同-h)
test –o File #文件的属于有效用户ID
test –p File #文件是一个命名管道
test –r File #文件是否可读
test –s File #文件是否是非空白文件
test –t FD #文件描述符是在一个终端打开的
test –u File #文件存在并且设置了它的set-user-id位
test –w File #文件是否存在并可写
test –x File #文件属否存在并可执行
#!/bin/bash
if [ $1 -ge 0 ] && [ $1 -lt 10 ];
then echo "0 < $1 < 10"
fi
执行脚本:# ./sh.sh 3
0 < 3 < 10
if [条件判断表达式]; then
当条件表达式成立时,可以执行命令 fi
语法格式2:
if [条件判断表达式]; then
当条件表达式成立时,可以执行命令 else
当条件表达式不成立时,可以执行命令 fi
语法格式3:
if [条件判断表达式1]; then
当条件表达式1成立时,可以执行命令
elfi [条件判断表达式2]; then
当条件表达式2成立时,可以执行命令 else
当条件表达式1与2均不成立时,可执行命令 fi
实例:
#!/bin/bash
#if test -f "$1"
if [ -f "$1" ]
then echo "$1 is an ordinary file."
else echo "$1 is not ordinary file."
fi
执行脚本:# ./sh.sh hello.txt
hello.txt is not ordinary file.
case $变量名称 in
第一个变量内容)
程序段 (满足第一个变量内容)
;;
第二个变量内容)
程序段二(满足第二个变量内容)
;;
...
*)
程序段(均不满足前面的条件下)
...
;;
esac
语法分析:#!/bin/bash
echo "a) choice a"
echo "b) choice b"
echo "c) choice c"
echo -e "Please enter your choice:\c"
read menu_choice
case "$menu_choice" in
a) echo "you choice a" ;;
b) echo "you choice b" ;;
c) echo "you choice c" ;;
*) echo "sorry, choice not exist" ;;
esac
执行脚本# ./sh.sh
a) choice a
b) choice b
c) choice c
Please enter your choice:a
you choice a
while [条件判断表达式]
do
程序段
done
语法分析:
#!/bin/bash
echo "a) choice a"
echo "b) choice b"
echo "c) choice c"
echo -e "Please enter your choice:\c"
read menu_choice
while [ "$menu_choice" != "a" ] && [ "$menu_choice" != "b" ] && [ "$menu_choice" != "c" ]
do
echo -e "Please enter your choice (a/b/c) to stop this programe:\c"
read menu_choice
done
执行脚本# ./sh.sh
a) choice a
b) choice b
c) choice c
Please enter your choice:d
Please enter your choice (a/b/c) to stop this programe:a
until [条件判断表达式]
do
程序段
done
语法分析:
#!/bin/bash
echo "a) choice a"
echo "b) choice b"
echo "c) choice c"
echo -e "Please enter your choice:\c"
read menu_choice
until [ "$menu_choice" = "a" ] || [ "$menu_choice" = "b" ] || [ "$menu_choice" = "c" ]
do
echo -e "Please enter your choice (a/b/c) to stop this programe:\c"
read menu_choice
done
执行脚本:# ./sh.sh
a) choice a
b) choice b
c) choice c
Please enter your choice:d
Please enter your choice (a/b/c) to stop this programe:a
for [条件判断表达式]
do
程序段
done
语法分析:
#!/bin/bash
for num in 1 2 3 4 5
do
echo "$num"
done
执行脚本:# ./sh.sh
1
2
3
4
5
break [ n ]
命令中 n 表示要跳出几层循环,默认值是 1,表示只跳出一层循环
continue [ n ]
命令中 n 表示从包含 continue 语句的最内层循环体向外跳到第几层循环,默认值为 1,循环层数由内向外编号。
[function]函数名()
{
命令表 (statements)
}
语法分析:
return [ n ]
命令中,n 值是退出函数时的退出值(退出状态),即 $? 的值。当 n 值缺省时,退出值是最后一个命令执行结果。
函数应先定义,后使用。调用函数时,我们可以直接利用函数名,如 foo 不必带圆括号,就像一般命令那样使用。使用函数的最大作用就是可以简化很多代码,这在较大的 shell 脚本设计中国可能会更加明显。#!/bin/bash
first ()
{
echo "*****************************"
}
second ()
{
echo "============================="
}
trird ()
{
echo -e "\fhello world!\f"
}
first
second
trird
second
first
执行脚本:# ./sh.sh
*****************************
=============================
hello world!
=============================
*****************************
#!/bin/sh
e=""
m=""
n="xxxx"
while [ 1 ]
do
n="xxxx"
e=`fdisk -l | grep /dev/sdb1 | awk '{print $1}'`
if [ -z "$e" ];then
echo "####no sd ####"
else
echo "####have sd !####"
m=`df | grep media | awk '{print $1}'`
if [ -n "$m" ];then
umount $m
fi
#umount $e
echo "####start to format sd with ext3 filesystem!####"
mkfs.ext3 "$e"
echo "####end format sd!####"
#echo "copy zslf_app_dir"
mount $e /media/mmcblk0p1
mkdir /media/mmcblk0p1/bak
mkdir /media/mmcblk0p1/video
mkdir /media/mmcblk0p1/video/temp
umount $e
echo "please remove sd!"
fi
sleep 5
if [ -n "$e" ];then
while [ -n "$n" ]
do
n=`fdisk -l | grep /dev/sdb1 | awk '{print $1}'`
sleep 5
done
fi
done
#!/bin/sh
php_config_copy ()
{
echo "start copy the config file from jyzc to thttpd/www."
cp /jyzc/config/encodeCfg.xml /jyzc/thttpd-php/www/config/encodeCfg.xml
cp /jyzc/config/devInfo.txt /jyzc/thttpd-php/www/config/devInfo.txt
cp /etc/network/interfaces /jyzc/thttpd-php/www/config/interfaces
}
ppp_config_copy
{
echo "start copy th config file from jyzc/config to /etc/ppp/peers."
cp /jyzc/config/ppp-off /etc/ppp/peers/
cp /jyzc/config/ppp0_on_state.txt /etc/ppp/peers/
}
echo "############## jyzc ##################"
php_config_copy
ppp_config_copy
route del default
mknod /dev/pio c 203 0
cd /etc/ppp/information/
rm -rf addr.txt
echo " " > /etc/ppp/information/state.txt
#mknod -m 660 /dev/ttyUSB0 c 188 0
#mknod -m 660 /dev/ttyUSB1 c 188 1
#mknod -m 660 /dev/ttyUSB2 c 188 2
#mknod -m 660 /dev/ttyUSB3 c 188 3
#mknod -m 660 /dev/ttyUSB4 c 188 4
#mknod -m 660 /dev/ttyUSB5 c 188 5
/jyzc/ntpdate_first.sh &
./state.sh &
/jyzc/create_mem &
sleep 5
#/jyzc/3gStrength &
/jyzc/thttpd-php/sbin/thttpd -C /jyzc/thttpd-php/thttpd.conf
sleep 1
vsftpd
sleep 2
mkdir /media/mmcblk0p1/bak
cd /jyzc/config/
./srv 8000 &
./pio_led &
/jyzc/rm_pic.sh &
#4G LTE Setup Script
#/jyzc/LteSetUp.sh &
#sleep 10
#i=`cat /proc/bus/usb/devices | grep ProdID=1573`
#./MU609-3gState &
insmod /jyzc/usbnet.ko
insmod /jyzc/cdc_encap.ko
insmod /jyzc/cdc_ether.ko
sleep 2
/jyzc/ME3760Connect
echo "######## encode #########"
cd /jyzc
./initial.sh
./get_ntptime &
./encode &
./thttpd.sh &