欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

探究正常運(yùn)行的shell腳本為啥有時(shí)會(huì)報(bào)錯(cuò)

 更新時(shí)間:2023年08月04日 11:32:36   作者:Mochi_Cruise  
正常運(yùn)行的shell腳本為啥有時(shí)會(huì)報(bào)錯(cuò)呢,這篇文章主要就是來(lái)和大家探索一下報(bào)錯(cuò)的原因以及解決方法,感興趣的小伙伴可以跟隨小編一起了解一下

1、話題引入

案例1

在項(xiàng)目用java寫過(guò)一套執(zhí)行Shell腳本的邏輯,在一臺(tái)centos7.5的服務(wù)器上正常運(yùn)行,部署在一臺(tái)ubuntu18.04的機(jī)器上卻運(yùn)行失敗。

案例2

寫一個(gè)腳本test.sh,使用不同的命令執(zhí)行

mochicruise@MochideMacBook-Pro Shell % cat test.sh 
#!/bin/bash
echo "---------"
source 233
echo "error"
?

用不同的命令執(zhí)行

mochicruise@MochideMacBook-Pro Shell % ./test.sh 
---------
./test.sh: line 3: 233: No such file or directory
error
mochicruise@MochideMacBook-Pro Shell % sh test.sh 
---------
test.sh: line 3: 233: No such file or directory
mochicruise@MochideMacBook-Pro Shell % bash test.sh 
---------
test.sh: line 3: 233: No such file or directory
error

上面兩個(gè)案例執(zhí)行的結(jié)果不同,其原因是Shell類型及兼容模式不同,執(zhí)行的結(jié)果也有不同

2、Shell類型

列舉一些常見(jiàn)的Shell

sh(Bourne Shell):

Bourne Shell是Unix系統(tǒng)中最早的Shell之一,它的語(yǔ)法相對(duì)簡(jiǎn)單,功能較為基礎(chǔ)。它是其他Shell的基礎(chǔ),許多腳本和系統(tǒng)工具仍然使用Bourne Shell語(yǔ)法。

bash(Bourne Again Shell):

bash是最常用的Shell之一,幾乎在所有Linux發(fā)行版中都默認(rèn)安裝。它是Bourne Shell的增強(qiáng)版本,提供了更多功能和改進(jìn)。bash具有強(qiáng)大的腳本編程能力,支持命令歷史、自動(dòng)補(bǔ)全、別名等特性。

dash(Debian Almquist Shell):

是一種輕量級(jí)的Shell,它是Bourne Shell的替代品,它遵循POSIX標(biāo)準(zhǔn),旨在提供更快的啟動(dòng)速度和更低的內(nèi)存消耗。dash主要用于Unix和Linux系統(tǒng)中的腳本執(zhí)行,特別是在Debian和Ubuntu等發(fā)行版中作為默認(rèn)的/bin/sh解釋器。缺點(diǎn)是較少的擴(kuò)展功能。

csh(C Shell):

C Shell是一種基于C語(yǔ)言語(yǔ)法的Shell,它引入了許多C語(yǔ)言的特性,如變量聲明和控制結(jié)構(gòu)。C Shell的語(yǔ)法更接近于C語(yǔ)言,因此對(duì)于熟悉C語(yǔ)言的開(kāi)發(fā)人員來(lái)說(shuō)更容易上手。

ksh(Korn Shell):

Korn Shell是由AT&T Bell實(shí)驗(yàn)室的David Korn開(kāi)發(fā)的Shell,它結(jié)合了Bourne Shell和C Shell的特性,并添加了一些新功能。Korn Shell在功能和易用性方面都比較強(qiáng)大,是許多Unix系統(tǒng)的默認(rèn)Shell。

tcsh(TENEX C Shell):

tcsh是C Shell的擴(kuò)展版本,提供了更多的功能和改進(jìn)。它增加了命令行編輯、命令補(bǔ)全、歷史命令等特性,使得交互式使用更加方便。

zsh(Z Shell):

zsh可以說(shuō)是Shell重的極品,它是bash和Korn Shell的擴(kuò)展版本。它在自動(dòng)補(bǔ)全、命令歷史、插件和擴(kuò)展、主題和提示符、通配符方面都很強(qiáng)大,它的強(qiáng)大功能和用戶友好性使得命令行操作更加高效和愉快。

3、腳本執(zhí)行

Linux命令行與Shell腳本編程大全 第4版》P217第11.2章中說(shuō)“在創(chuàng)建 Shell 腳本文件時(shí),必須在文件的第一行指定要使用的 Shell,格式如下: #!/bin/bash”,其實(shí)這個(gè)不是必要的,即使不設(shè)置,也是可以執(zhí)行的。

3.1、Sha-Bang 是什么

Sha-Bang 就是通常腳本開(kāi)頭的頭兩個(gè)字符“#!”連在一起的讀音,在 Unix 術(shù)語(yǔ)中, # 號(hào)通常稱為 sharp,hash 或 mesh;而嘆號(hào)!則常常稱為 bang。

一般說(shuō)來(lái),任何一個(gè)腳本程序都應(yīng)以其為起始。它們就是腳本文件有執(zhí)行權(quán)限就能被直接執(zhí)行的秘密所在。“#!”是一個(gè)魔數(shù)(Magic,其值為 0x23,0x21),可執(zhí)行文件在被讀取的時(shí)候,內(nèi)核通過(guò)這個(gè)特定的數(shù)字組合開(kāi)頭識(shí)別出這是一個(gè)需要運(yùn)行解釋器腳本,并且根據(jù)約定將其后的字符串在讀到換行以前解釋為該腳本需要的解釋器所在路徑。系統(tǒng)會(huì)按照路徑調(diào)用解釋器之后再把整個(gè)文本的內(nèi)容傳遞給解釋器。腳本內(nèi)容如何解釋,執(zhí)行什么動(dòng)作就交給了解釋器。所以,Shell 腳本雖然是一個(gè)純文本文件,依然可以正常執(zhí)行。

可以在2014年版的 Advanced bash scripting guide(revision 10)中,找到 #! 的相關(guān)說(shuō)明,其中,Chapter 2. Starting Off With a Sha-Bang 詳細(xì)解釋了 “#!/xxx” 的用法。

sha-bang 這個(gè)符號(hào)通常在 Unix 系統(tǒng)的腳本中第一行開(kāi)頭中寫到,它指明了執(zhí)行這個(gè)腳本文件的解釋程序:

  • 如果腳本文件中沒(méi)有 #! 這一行,那么它執(zhí)行時(shí)會(huì)默認(rèn)用當(dāng)前 Shell 去解釋這個(gè)腳本(即:$SHELL 環(huán)境變量)。
  • 如果 #! 之后的解釋程序是一個(gè)可執(zhí)行文件,那么執(zhí)行這個(gè)腳本時(shí),它就會(huì)把文件名及其參數(shù)一起作為參數(shù)傳給那個(gè)解釋程序去執(zhí)行。
  • 如果 #! 指定的解釋程序沒(méi)有可執(zhí)行權(quán)限,則會(huì)報(bào)錯(cuò)“bad interpreter: Permission denied”。如果#!指定的解釋程序不是一個(gè)可執(zhí)行文件,那么指定的解釋程序會(huì)被忽略,轉(zhuǎn)而交給當(dāng)前的 Shell 去執(zhí)行這個(gè)腳本。
  • 如果 #! 指定的解釋程序不存在,那么會(huì)報(bào)錯(cuò) “bad interpreter: No such file or directory”。注意:#!之后的解釋程序,需要寫其絕對(duì)路徑(如:#!/bin/bash),它是不會(huì)自動(dòng)到 $PATH 中尋找解釋器的。
  • 當(dāng)然,如果你使用 bash test.sh 這樣的命令來(lái)執(zhí)行腳本,那么#!這一行將會(huì)被忽略掉,解釋器當(dāng)然是用命令行中顯式指定的 bash。
  • 使用 #!/usr/bin/env 這樣的腳本解釋器名稱,可以實(shí)現(xiàn)在不同平臺(tái)上都能正確找到解釋器。
  • 如果腳本文件是以 UTF-8 的 BOM(0xEF 0xBB 0xBF) 開(kāi)頭的,那么 exec 函數(shù)將不會(huì)啟動(dòng) sha-bang 指定的解釋器來(lái)執(zhí)行該腳本。因此,Linux 的腳本文件不應(yīng)在文件開(kāi)頭包含 UTF-8 的 BOM。

3.2、腳本執(zhí)行原理

用戶可以通過(guò)Shell和操作系統(tǒng)交互,通俗的講Shell就是一個(gè)解釋器,當(dāng)我們輸入命令Shell就解釋執(zhí)行,Shell有很多版本,在命令行中輸入一條命令可以查看當(dāng)前正在使用的Shell

mochicruise@MochideMacBook-Pro Shell % echo $SHELL
/bin/zsh

Shell和操作系統(tǒng)的交互有兩種方式

交互式(Interactive):

Shell的作用是解釋執(zhí)行用戶的命令,用戶輸入一條命令,Shell就解釋執(zhí)行一條,這種方式稱為交互式(Interactive)。

批處理(Batch):

Shell還有一種執(zhí)行命令的方式稱為批處理(Batch),用戶事先寫一 個(gè)Shell腳本,其中有很多條命令,讓Shell一次把這些命令執(zhí)行完,而不必一條一條地敲命令。Shell腳本和編程語(yǔ)言很相似,也有變量和流程控制語(yǔ)句,但Shell腳本是解釋執(zhí)行的,不需要編譯,Shell程序從腳本中一行一行讀取并執(zhí)行這些命令,相當(dāng)于一個(gè)用戶把腳本中的命令一行一 行敲到Shell提示符下執(zhí)行。

在Linux命令輸入./ test.sh 時(shí),我們?cè)谶@個(gè)文本文件開(kāi)頭指定了bash為默認(rèn)的解釋器,因此當(dāng)前的交互式Shell會(huì)fork一個(gè)子進(jìn)程,用bash解釋器的代碼去替換(也就是exec),而這個(gè)文本文件被當(dāng)作是命令行參數(shù)傳給這個(gè)子bash,等這個(gè)子bash執(zhí)行完就會(huì)到我們的交互式bash了。

3.3、./script.sh、sh script.sh 和 /bin/sh script.sh 的區(qū)別

3.3.1、執(zhí)行./script.sh:

./script.sh 會(huì)優(yōu)先判斷腳本有沒(méi)有制定Shell類型,如果指定的話會(huì)以指定的Shell作為解釋器,test.sh第一行定義了#!/bin/bash,那./test.sh就會(huì)以bash去執(zhí)行腳本。如果沒(méi)有則會(huì)以當(dāng)前環(huán)境的默認(rèn)Shell去執(zhí)行腳本

#定義sha-bang
mochicruise@MochideMacBook-Pro Shell % ./test.sh ? 
---------
./test.sh: line 3: 233: No such file or directory
error
mochicruise@MochideMacBook-Pro Shell % sed '1d' test.sh > test2.sh
#不定義sha-bang
mochicruise@MochideMacBook-Pro Shell % chmod u+x test2.sh 
mochicruise@MochideMacBook-Pro Shell % ./test2.sh 
---------
./test2.sh: line 2: 233: No such file or directory
#生成test3.sh 并將它的sha-bang定義#!/bin/zsh
mochicruise@MochideMacBook-Pro Shell % sed '1s/bash/zsh/' test.sh > test3.sh
mochicruise@MochideMacBook-Pro Shell % cat test3.sh 
#!/bin/zsh
echo "---------"
source 233
echo "error"
mochicruise@MochideMacBook-Pro Shell % chmod u+x test3.sh 
mochicruise@MochideMacBook-Pro Shell % ./test3.sh ? ? ? ?
---------
./test3.sh: 3: source: not found
error

可以看到兩個(gè)腳本執(zhí)行的結(jié)果不一樣,是因?yàn)閠est2.sh是以環(huán)境默認(rèn)Shell執(zhí)行的

在centos中默認(rèn)為bash(Bourne Again Shell)的環(huán)境中,不定義sha-bang,也是打印出了“error”;這個(gè)應(yīng)該是zsh沒(méi)有完全遵循POSIX標(biāo)準(zhǔn)

看一下當(dāng)前環(huán)境變量的默認(rèn)Shell

mochicruise@MochideMacBook-Pro Shell % echo $Shell
/bin/zsh

也可以用echo $0查看腳本本身的名字,前面說(shuō)在命令行是Shell與操作系統(tǒng)交互的一種方式

#zsh前面帶個(gè)“-”是表示當(dāng)前是log Shell
mochicruise@MochideMacBook-Pro Shell % echo $0
-zsh
mochicruise@MochideMacBook-Pro Shell % zsh
mochicruise@MochideMacBook-Pro Shell % echo $0 ? ?
zsh
?
#在manpage的INVOCATION中有說(shuō)明相關(guān)信息
INVOCATION
A  login Shell is one whose first character of argument zero is a -, or one started with the --login option.

如果在test.sh 中加入一行“ echo $0”的話會(huì)輸出test.sh腳本的名字。即執(zhí)行./test.sh那么會(huì)輸出一行“ ./test.sh”,如果執(zhí)行“sh test.sh”那么會(huì)輸出一行“‘test.sh”。

3.3.2、執(zhí)行sh script.sh:

先用which 查看執(zhí)行命令sh的位置

mochicruise@MochideMacBook-Pro Shell % which sh
/bin/sh

然后看是否有鏈接文件,當(dāng)前系統(tǒng)是macOS Ventura 13.4 ,并沒(méi)有指向其他文件,

mochicruise@MochideMacBook-Pro Shell % ls -al /bin/sh
-rwxr-xr-x  1 root  wheel  134000 Jun 15 18:08 /bin/sh

而在CentOS上sh最終指向了/usr/bin/bash

[root@ecs-135733 ~]# which sh
/usr/bin/sh
[root@ecs-135733 ~]# ll /usr/bin/sh
lrwxrwxrwx 1 root root 4 Feb 10 ?2022 /usr/bin/sh -> bash
[root@ecs-135733 ~]# which bash
/usr/bin/bash
[root@ecs-135733 ~]# ll /usr/bin/bash
-rwxr-xr-x 1 root root 964536 Nov 25 ?2021 /usr/bin/bash

用sh/bash/dash/zsh等執(zhí)行腳本時(shí),會(huì)用它最終指向的Shell執(zhí)行腳本,指定的sha-bang就不生效

mochicruise@MochideMacBook-Pro Shell % sh test.sh 
---------
test.sh: line 4: 233: No such file or directory
mochicruise@MochideMacBook-Pro Shell % bash test.sh 
---------
test.sh: line 3: 233: No such file or directory
error
mochicruise@MochideMacBook-Pro Shell % dash test.sh 
---------
test.sh: 3: source: not found
error

bash(Bourne Again Shell)和zsh(Z Shell)執(zhí)行的test.sh最終會(huì)把“error”打印出來(lái),而sh(Bourne Shell)和 執(zhí)行的腳本則不會(huì);那么從上面看sh最終指向bash,為什么執(zhí)行結(jié)果和bash不一樣?這個(gè)就是與是否遵循POSIX標(biāo)準(zhǔn)有關(guān),下面會(huì)講到

用dash和bash來(lái)驗(yàn)證sha-bang不生效的情況

mochicruise@MochideMacBook-Pro Shell % cat test4.sh 
#!/bin/bash
select var in {1,2,4,6}
do
echo $var
break
done
mochicruise@MochideMacBook-Pro Shell % bash ? ? ? ?
The default interactive Shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
bash-3.2$ ./test4.sh 
1) 1
2) 2
3) 4
4) 6
#? 2
2
bash-3.2$ dash test4.sh 
test4.sh: 2: select: not found
test4.sh: 3: Syntax error: "do" unexpected

ubuntu默認(rèn)的Shell是dash,作為兩種常用的Shell,概述一下dash和bash的區(qū)別

bashdash
function為關(guān)鍵字沒(méi)有
select var in list; do command; done不支持, 替代方法:采用while+read+case來(lái)實(shí)現(xiàn)
echo {0..10}支持{n..m}展開(kāi)不支持,替代方法, 采用seq外部命令
here string支持不支持, 替代方法:可采用here documents
>&word重定向標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤當(dāng)word為非數(shù)字時(shí),>&word變成重定向標(biāo)準(zhǔn)錯(cuò)誤和標(biāo)準(zhǔn)輸出到文件word>&word, word不支持非數(shù)字, 替代方法: >word 2>&1; 常見(jiàn)用法 >/dev/null 2>&1
數(shù)組支持?jǐn)?shù)組, bash4支持關(guān)聯(lián)數(shù)組不支持?jǐn)?shù)組,替代方法, 采用變量名+序號(hào)來(lái)實(shí)現(xiàn)類似的效果
字符串?dāng)U展支持parameter:offset:length,{parameter:offset:length},parameter:offset:length,{parameter:offset}不支持, 替代方法:采用expr或cut外部命令代替
大小寫轉(zhuǎn)換支持parameterpattern,{parameter^pattern},parameterpattern,{parameter^^pattern}和parameter,pattern,{parameter,pattern},parameter,pattern,{parameter,,pattern}不支持

3.3.3、執(zhí)行/bin/sh script.sh

這個(gè)要看sh的執(zhí)行路徑是否是/bin/sh,或者sh的執(zhí)行路徑的包是否和/bin/sh的包內(nèi)容完全一致,如果完全一致,那么兩者執(zhí)行效果完全一樣。

3.3.4、/bin/sh script.sh 和 /usr/bin/sh script.sh的區(qū)別

/bin/bash和/usr/bin/bash 都是來(lái)自bash的rpm的包,內(nèi)容完全一致

[root@ecs-135733 mnt]# rpm -qf /bin/bash
bash-4.2.46-35.el7_9.x86_64
[root@ecs-135733 mnt]# rpm -qf /usr/bin/bash
bash-4.2.46-35.el7_9.x86_64
[root@ecs-135733 mnt]# diff /bin/bash /usr/bin/sh && echo 'no diff'
no diff

3.4、POSIX標(biāo)準(zhǔn)

下面這個(gè)文檔講述了啟用POSIX模式后的差異

bash有三種方式使其遵守POSIX標(biāo)準(zhǔn)

  • 啟動(dòng)時(shí)在sha-bang后面增加--posix參數(shù)
  • 啟動(dòng)后,使用set -o posix指令
  • 以sh來(lái)啟動(dòng)

只要程序以sh的方式啟動(dòng),就會(huì)遵循POSIX標(biāo)準(zhǔn),與路徑無(wú)關(guān),在manpage中可以看到

    If bash is invoked with the name sh, it tries to mimic the startup behavior of historical versions of sh as closely as possible, while conforming to the POSIX standard as well.  When invoked as an interactive login Shell, or a non-interactive Shell with the --login option, it first attempts to read and execute commands from /etc/profile and ~/.profile, in that order.  The --noprofile option may be used to inhibit this behavior.  When invoked as an interactive Shell with the name sh, bash looks for the variable ENV, expands its value if it is defined, and uses the expanded value as the name of a file to read and execute.  Since a Shell invoked as sh does not attempt to read and execute commands from any other startup files, the --rcfile option has no effect.  A non-interactive Shell invoked with the name sh does not attempt to read any other startup files.  When invoked as sh, bash enters posix mode after the startup files are read. 
    When bash is started in posix mode, as with the --posix command line option, it follows the POSIX standard for startup files.  In this mode, interactive Shells expand the ENV variable and commands are read and executed from the file whose name is the expanded value.  No other startup files are read.

3.5、Shell類型選取

從一個(gè)交互式終端的角度來(lái)講,zsh更為強(qiáng)大,也越來(lái)越受人們的歡迎,所以在命令行可以用zsh。而bash更加符合posix標(biāo)準(zhǔn),因此bash更適合做腳本解釋器寫,所以在編寫Shell腳本是還是寫上#!/bin/bash。

到此這篇關(guān)于探究正常運(yùn)行的shell腳本為啥有時(shí)會(huì)報(bào)錯(cuò)的文章就介紹到這了,更多相關(guān)shell腳本報(bào)錯(cuò)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論