首页 > 系统相关 >shell的使用

shell的使用

时间:2024-08-30 16:27:08浏览次数:5  
标签:bin 输出 shell echo usr 使用 var bash

第一个shell程序介绍

#!/bin/bash
# This is a very simple example
echo "Hello World"
  • #!/bin/bash 表明该文件是一个 BASH 程序,需要由 /bin 目录下的 bash 程序来解释执行。BASH 这个程序一般是存放在 /bin 目录下,如果你的 Linux 系统比较特别,bash 也有可能被存放在 /sbin 、/usr/local/bin 、/usr/bin 、/usr/sbin 或 /usr/local/sbin 这样的目录下;如果还找不到,你可以用 "locate bash" "whereis bash" 这三个命令找出 bash 所在的位置;如果仍然找不到,那你可能需要自己动手安装一个 BASH 软件包了

  • 第二行的 “# This is a ...“ 就是 BASH 程序的注释,在 BASH 程序中从#号(注意:后面紧接着是!号的除外)开始到行尾的多有部分均被看作是程序的注释

  • 三行的 echo 语句的功能是把 echo 后面的字符串输出到标准输出中去
  • 需要注意的是BASH 中的绝大多数语句结尾处都没有分号。
执行程序

显式指定 BASH 去执行

$ bash hello
$ sh hello
 
这里 sh 是指向 bash 的一个链接,
lrwxrwxrwx 1 root root 4 Aug 20 05:41 /bin/sh -> bash

隐式执行

$ chmod a+x hello
$ ./hello

关于输入、输出和错误输出
在字符终端环境中,标准输入/标准输出的概念很好理解。输入即指对一个应用程序 或命令的输入,无论是从键盘输入还是从别的文件输入;
输出即指应用程序或命令产生的一些信息;与 Windows 系统下不同的是,Linux 系统下还有一个标准错误输出的概念,这个概念主要是为程序调试和系统维护目的而设置的,错误输出于标准输出分开可以让一些高级的错误信息不干扰正常的输出 信息,从而方便一般用户的使用

在 Linux 系统中:标准输入(stdin)默认为键盘输入;标准输出(stdout)默认为屏幕输出;标准错误输出(stderr)默认也是输出到屏幕.

在 BASH 中使用这些概念时,

  • 将标准输出表示为 1
  • 将标准错误输出表示为2
举例说明 输入、输出及标准错误输出主要用于 I/O 的重定向。先看这个例子:
$ ls > ls_result
$ ls -l >> ls_result

上面这两个命令分别将 ls 命令的结果输出重定向到 ls_result 文件中和追加到 ls_result 文件中,而不是输出到屏幕上。

  • >就是输出(标准输出和标准错误输出)重定向的代表符号
  • >> 则表示不清除原来的而追加输入

下面再来看一个稍微复杂的例子:

$ find /home -name lost* 2> err_result

这个命令在 > 符号之前多了一个 22> 表示将标准错误输出重定向到err_result

要想让标准错误输出和标准输入一样都被存入到文件中,那该怎么办呢?看下面这个例子:

$ find /home -name lost* > all_result 2>& 1

上面这个例子中将首先将标准错误输出也重定向到标准输出中,再将标准输出重定向到 all_result 这个文件中。这样我们就可以将所有的输出都存储到文件中了

如果那些出错信息并不重要,下面这个命令可以让你避开众多无用出错信息的干扰:

$ find /home -name lost* 2> /dev/null

再试验一下如下几种重定向方式,看看会出什么结果,为什么?

$ find /home -name lost* > all_result 1>& 2
$ find /home -name lost* 2> all_result 1>& 2
$ find /home -name lost* 2>& 1 > all_result
SHELL特殊参数【命令行参数】

shell 针对参数已经有设定好一些变量名称,对应如下:

/path/to/scriptname opt1 opt2 opt3 opt4 
$0 $1 $2 $3 $4

执行的脚本档名为 $0 这个变量,第一个接的参数就是 $1 后面类推

特殊变量

$# :代表后接的参数『个数』,以上面为例这里显示为『 4 』;
$@ :代表『 "$1" "$2" "$3" "$4" 』之意,每个变量是独立的(用双引号括起来);
$* :代表『 "$1c$2c$3c$4" 』,其中 c 为分隔字符,默认为空格键, 所以本例中代表『 "$1 $2 $3 $4" 』之意。
变量赋值和引用

Shell编程中,使用变量无需事先声明,同时变量名的命名须遵循如下规则:

  • 首个字符必须为字母(a-z,A-Z) 或者_
  • 中间不能有空格,可以使用下划线(_)
  • 不能使用其他标点符号
  • 需要给变量赋值时,可以这么写:
变量名=值

要取用一个变量的值,只需在变量名前面加一个$ ( 注意: 给变量赋值的时候,不能在”=”两边留空格 )

#!/bin/bash
# 对变量赋值:
a="hello world" #等号两边均不能有空格存在
 
# 打印变量a的值:
echo "A is:" $a
有时候变量名可能会和其它文字混淆,比如:
num=2
echo "this is the $numnd"

上述脚本并不会输出this is the 2nd. 这是由于shell会去搜索变量numnd的值,而实际上这个变量此时并没有值。这时,我们可以用花括号来告诉shell要打印的是num变量:

num=2
echo "this is the ${num}nd"
其输出结果为:this is the 2nd
 
注意花括号的位置:
num=2
echo "this is the {$num}nd"
其输出结果为:this is the {2}nd

需要注意shell的默认赋值是字符串赋值。比如:

var=1
var=$var+1
echo $var
打印出来的不是2而是1+1。

为了达到我们想要的效果有以下几种表达方式:

let "var+=1"
var="$[$var+1]"
((var++))
var=$(($var+1))
var="$(expr "$var" + 1)"
SHELL里的流程控制 If语句

if表达式如果条件为真,则执行then后的部分:

if ....; then
....
elif ....; then
....
else
....
fi

  

大多数情况下,可以使用测试命令来对条件进行测试,比如可以比较字符串、判断文件是否存在及是否可读等等

通常用[ ] 来表示条件测试,==注意这里的空格很重要,要确保方括号前后的空格==

[ -f "somefile" ] :判断是否是一个文件
[-d "Directory"] : 判断目录是否存在
[ -x "/bin/ls" ] :判断/bin/ls是否存在并有可执行权限
[ -n "$var" ] :判断$var变量是否有值

下面是一个简单的if语句:

#!/bin/bash
 
if [ ${SHELL} == "/bin/bash" ]; then
echo "your login shell is the bash (bourne again shell)"
else
echo "your login shell is not bash but ${SHELL}"
fi
case 语句
case表达式可以用来匹配一个给定的字符串,而不是数字(可别和php语言里的switch...case混淆)

 

case ... in
do something here ;;
esac

当给程序输入start时,显示service is running!;

#!/bin/bash
read -p "请输入参数:" var
case $var in
start)
echo service is running
;;
stop)
echo service is stoped
;;
reload)
echo service is reload
;;
*)
echo xxxxx
;;
esac
while/for循环

在shell中,可以使用如下循环:

while [ condition ] <==中括号内的状态就是判断表达式 
do <== do 是循环的开始!
程序段落
done <== done 是循环的结束 

只要测试表达式条件为真,则while循环将一直运行。关键字break用来跳出循环,而关键字continue则可以跳过一个循环的余下部分,直接跳到下一次循环中。

for循环会查看一个字符串列表(字符串用空格分隔),并将其赋给一个变量:

for var in ....; do
....
done

下面的示例会把A B C分别打印到屏幕上:

#!/bin/bash
 
for var in A B C; do
echo "var is $var"
done

下面是一个实用的脚本showrpm,其功能是打印一些RPM包的统计信息:

#!/bin/bash
 
# list a content summary of a number of RPM packages
# USAGE: showrpm rpmfile1 rpmfile2 ...
# EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
 
for rpmpackage in "$@"; do
if [ -r "$rpmpackage" ];then
echo "=============== $rpmpackage =============="
rpm -qi -p $rpmpackage
else
echo "ERROR: cannot read file $rpmpackage"
fi
done

这里出现了第二个特殊变量$@,该变量包含有输入的所有命令行参数值。如果你运行showrpm openssh.rpm w3m.rpm webgrep.rpm,那么 $@ 就包含有 3 个字符串,即openssh.rpmw3m.rpm和 webgrep.rpm

Shell里的一些特殊符号

先来看一个例子,假设在当前目录下有两个jpg文件:mail.jpgtux.jpg

#!/bin/bash
 
echo *.jpg
运行结果为:
mail.jpg tux.jpg

引号(单引号和双引号)可以防止通配符*的匹配:

#!/bin/bash
 
echo "*.jpg"
echo '*.jpg'
其运行结果为:
*.jpg
*.jpg

其中单引号更严格一些,它可以防止任何特殊字符匹配:

#!/bin/bash
 
echo $SHELL
echo "$SHELL"
echo '$SHELL'
运行结果为:
/bin/bash
/bin/bash
$SHELL

此外还有一种防止这种扩展的方法,即使用转义字符——反斜杆:\


echo \*.jpg
echo \$SHELL
输出结果为:
*.jpg
$SHELL
Shell里的函数

如果你写过比较复杂的脚本,就会发现可能在几个地方使用了相同的代码,这时如果用上函数,会方便很多。函数的大致样子如下:

function fname() { 
程序段
}

函数没有必要声明。只要在执行之前出现定义就行

那个 fname 就是我们的自定义的执行指令名称,而程序段就是我们要他执行的内容了。 要注意的是,因为 shell script 的执行方式是由上而下,由左而史, 因此在 shell script 当中的 function 的设定一定要在程序的最前面, 这样才能够在执行时被找找到可用的程序段

我们自定义一个名为 printit 的函数来使用

#!/bin/bash
 
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
 
function printit(){
echo -n "Your choice is " # 加上 -n 可以不断行继续在同一行显示
}
 
echo "This program will print your selection !"
 
case $1 in
"one")
printit; echo $1 | tr 'a-z' 'A-Z' # 将参数做大小写转换!
;;
"two")
printit; echo $1 | tr 'a-z' 'A-Z'
;;
"three")
printit; echo $1 | tr 'a-z' 'A-Z'
;;
*)
echo "Usage $0 {one|two|three}"
;;
esac

以上面的例子来说,做了一个函数名称为 printit ,所以,当我在后续的程序段里面, 叧要执行 printit 的话,就表示我的 shell script 要去执行『 function printit .... 』 里面的那几个程序段落

另外, function 也是拥有内建变量, 他的内建变量与 shell script 很类似, 函数名称代表示 $0 ,而后续接变量也是以 $1$2… 来取代的, 这里很容易搞错, 因为『 function fname() { 程序段 } 』内的 $0$1… 等等与 shell script 的 $0 是不同的。

以上面程序来说,假如我执行:『 sh test.sh one 』 这表示在 shell script 内的 $1 为 “one” 这个字符串。但是在 printit() 内的 $1 则不这个 one 无关。

[root@www scripts]# vi sh12-3.sh
#!/bin/bash
 
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
 
function printit(){
echo "Your choice is $1" # 这个 $1 必须要参考底下指令的下达
}
 
echo "This program will print your selection !"
case $1 in
"one")
printit 1 # 请注意, printit 指令后面还有接参数!
;;
"two")
printit 2
;;
"three")
printit 3
;;
*)
echo "Usage $0 {one|two|three}"
;;
esac

在上面的例子当中,如果你输入『 sh sh12-3.sh one 』就会出现『 Your choice is 1 』的字样, 为什么是 1 呢?因为在程序段落当中,我们是写了『 printit 1 』那个1 就会成为 function 当中的 $1

SHELL脚本示例 脚本调试

最简单的调试方法当然是使用echo命令。你可以在任何怀疑出错的地方用echo打印变量值,这也是大部分shell程序员花费80%的时间用于调试的原因。Shell脚本的好处在于无需重新编译,而插入一个echo命令也不需要多少时间。

[root@www ~]# sh [-nvx] scripts.sh
选项不参数:
-n :不要执行 script,仅查询语法的问题;
-v :再执行 sccript 前,先将 scripts 的内容输出到屏幕上;
-x :将使用到的 script 内容显示到屏幕上,这是很有用的参数!

范例一:测试 sh16.sh 有无语法的问题?

[root@www ~]# sh -n sh16.sh
# 若语法没有问题,则不会显示任何信息!

范例二:将 sh15.sh 的执行过程全部列出来~

[root@www ~]# sh -x sh15.sh
+
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/root/bin
+ export PATH
+ for animal in dog cat elephant
+ echo 'There are dogs.... '
There are dogs....
+ for animal in dog cat elephant
+ echo 'There are cats.... '
There are cats....
+ for animal in dog cat elephant
+ echo 'There are elephants.... '
There are elephants....

nginx日志按日期自动切割脚本如下

#nginx日志切割脚本
#!/bin/bash
#设置日志文件存放目录
logs_path="/usr/local/nginx/logs/"
#设置pid文件
pid_path="/usr/local/nginx/nginx.pid"
 
#重命名日志文件
mv ${logs_path}access.log ${logs_path}access_$(date -d "yesterday" +"%Y%m%d").log
 
#向nginx主进程发信号重新打开日志
kill -USR1 `cat ${pid_path}`
 
crontab 设置作业
0 0 * * * bash /usr/local/nginx/nginx_log.sh
这样就每天的0点0分把nginx日志重命名为日期格式,并重新生成今天的新日志文件。

  

标签:bin,输出,shell,echo,usr,使用,var,bash
From: https://www.cnblogs.com/596014054-yangdongsheng/p/10638909.html

相关文章

  • Havoc的demon开发环境配置(使用vscode二开beacon)
    二开havoc需要看一下之前的几篇文章,感谢chatAI让我少吃了很多苦头,二开beacon(demon)环境终于搭建好了,之前想过使用ollvm编译demon,以便于源码级的混淆,但是试了以下,llvm能让人吃更多苦头,我没有测试过demon能不能使用llvm编译,因为llvm本身就很难编译成功,以至于我不想在llvm上花费任......
  • Unity+QT, 如何把Unity打包出的exe嵌入QT程序中,并且使用Socket进行交互
    文章目录概要前期准备QT与Unity的Socket通信实际应用场景总结概要在VR开发中,常常需要桌面窗口和VR头盔使用者进行交互。通过将Unity制作的VR程序嵌入到QT应用程序窗口中,并使用Socket进行通信,可以实现这种交互。本文将介绍如何实现这一功能。前期准......
  • auto的使用场景
    auto的两面性合理使用auto不仅可以减少代码量,也会大大提高代码的可读性.但是事情总有它的两面性如果滥用auto,则会让代码失去可读性推荐写法这里推荐两种情况下使用auto一眼就能看出声明变量的初始化类型的时候比如迭代器的循环,用例如下#include<iostream>......
  • vue使用html2canvas将页面dom生成图片,解决页面中带有图片导出
    安装npm installhtml2canvas引入importhtml2canvas from 'html2canvas'使用this.$refs.canvasToPic是div的dom,只要在这个div中的区域都可以生成图片1this.$nextTick(()=>{2html2canvas(this.$refs.canvasToPic,{useCORS:true,logging:true}).......
  • Linux之shell脚本变量
    变量是脚本种的一种重要的属性,它可以存储值,这就大大的增加了脚本的灵活性使得脚本的应用更为广泛。自定义变量变量名=值(等号两边不能有空格)在linux内可以定义变量然后用$引用如果要在调用后面加字符之类的则可以使用${变量名}字符环境变量系统帮你定义好的变量例如US......
  • 使用Opatch命令报错:Java (1.7) could not be located. OPatch cannot proceed!
    问题描述[oracle@servernamedb]$OPatch/opatchversionOPatch/opatch:line839:[:toomanyargumentsOPatch/opatch:line839:[:toomanyargumentsJava(1.7)couldnotbelocated.OPatchcannotproceed!OPatchreturnswitherrorcode=1解决方案删除OPatch目录......
  • 软件解决显卡欺骗器,HDMI欺骗器,如何使用ToDesk免费功能
    大家遇到电脑没有显示屏,或者显示屏的显卡太老,无法正常打开时,常常会给自己备上一个显卡欺骗器和HDMI欺骗器,虽说它们都能模拟显示屏正常使用环境,但这种方法通常需要额外的硬件支持,对于急用的小伙伴来说,这个方法很难快速解决显示屏难题。小社长最近发现ToDesk远程控制软件的虚拟屏......
  • C#中通用返回对象Result<T>(定义及使用)
     1.定义返回对象//Result对象是一种显式表示成功结果或失败的类型//方法可以返回这个类,而不是引发异常。如果操作失败,则Result对象将包含错误消息或代码,但不包含异常publicclassResult<T>{publicTValue{get;}publicstringEr......
  • 【Unity踩坑记录】使用Rigidbody模拟跳跃时,刚体会突然上升
    最初的写法privatevoidFixedUpdate(){if(!isGrounded){return;}floatrawHorizontal=Input.GetAxis("Horizontal");floatrawVertical=Input.GetAxis("Vertical");Vector3localDirection=new(rawHorizon......
  • PowerShell Select-String:在字符串和文件中查找文本
    语法Select-String[-Culture<String>][-Pattern]<String[]>[-Path]<String[]>[-SimpleMatch][-CaseSensitive][-Quiet][-List][-NoEmphasis][-Include<String[]>][-Exclu......