Shell十三問 學習筆記

文本處理

less -SN # -N顯示行號,-S整齊,變?yōu)橐恍?grep -w  #精準匹配
grep '^#'  #匹配以#開頭的行。正則表達式
grep -v #排除匹配到的
alias del=rm #給rm起個別名del,del同樣發(fā)揮rm的作用
tr ';' '\t' # tr命令,把分號換成\t
sed 's/;/\t/g' #與上等效
cut -f1 gtf | sort | uniq -c | sort -k1,1 -r # uniq去重復的行,并計數(shù)。去重后再sort排序,按第一列的數(shù)值進行由大到小的排序,去除r參數(shù),由小到大
history | awk '{print $2}' | sort | uniq -c | sort -n -k1 # sort -n 按照數(shù)值的大小排序,按照第一列排序 -k1。
awk '{print $1}' tmp | paste -s -d + |bc #bc算術表達式,tmp的第一列為幾行數(shù)值,awk把第一列抽取出來,paste -s把第一列的數(shù)值變?yōu)橐恍校?d設置以+分割,最后變?yōu)?+2+3的形式,再通過bc求和。需要以*分割的話,設置-d '*'
awk '{if($3=="gene")print}' gtf | wc 

Shell腳本編程

cat /proc/cpuinfo | grep processor  #查看服務器有多少個線程 / 核
free -g  #查看服務器的內存
df -h  #查看服務器的硬盤 / 存儲
for i in {20..23};do echo SRR10395${i}_1_val_1.fq.gz ;done #注意i的大括號,因為后面接了字符
for bam in *bam;do echo ${bam%%.*} ; done  # bam文件格式為SRR1039510.bowtie.bam,一個%.*刪除了后一個.后的內容,兩個%即%%.*輸出的為SRR1039510
samtools idxstats SRR1039508.hisat.bam | awk -F '\t' '{s+=$3}END{print s}'  # s+=$3,把以\t分隔開后的第3列的數(shù)字加起來求和并輸出
export total_reads = $(samtools idxstats $bam | awk -F '\t' '{s+=$3}END{print s}' )  #賦值給total_reads。export后,可以對子
shell同樣起作用

Shell 十三問 學習筆記

shell and Carriage 關系

Shell會依據(jù)IFS(Internal Field Seperator)command line 所輸入的文字給拆解為字段(word). 然后在針對特殊的字符(meta)先做處理,最后在重組整行 command line。

分隔符號IFS是用來拆解command line中每一個詞 (word) 用的, 因為shell command line是按詞來處理的。 而CR(Carriage Return, 由Enter鍵產生)則是用來結束command line用的,這也是為何我們敲Enter鍵, 命令就會跑的原因。

""(雙引號) 與''(單引號) 差在哪?

hard quote:''(單引號),凡在 hard quote 中的所有 meta 均被關閉;
soft quote:""(雙引號),凡在 soft quote 中大部分 meta 都會被關閉,但某些會保留 (如 $);
escape: \ (反斜杠),只有在緊接在 escape(跳脫字符) 之后的單一 meta 才被關閉;

$ A=`B > C > ' $ echo "$A" B C
$ A="B > C > " $ echo $A B C
$ A=B\ > C\ > $ echo $A BC

soft quote 跟 hard quote 的不同,主要是對于某些 meta 的關閉與否,以 $ 來做說明:

$ A=B\ C
$ echo "$A"
B C
$ echo '$A'
$A

awk '{print $0}'
上面的 hard quote 應好理解,就是將原來的 {$} 這幾個 shell meta 關閉, 避免掉在 shell 中遭到處理,而完整的成為 awk 的參數(shù)中 command meta。

awk "{print \$0}" 1.txt 
awk \{print \$0\} 1.txt  #與上是等效的,注意`\`關閉了$ 字符

var=value 在 export 前后的差在哪?

所謂的變量,就是利用一個固定的"名稱"(name), 來存取一段可以變化的"值"(value)。

設定變量時:

  1. 等號左右兩邊不能使用分隔符號(IFS)name=value,也應避免使用shell的保留元字符(meta charactor);
  2. 變量的名稱(name)的首字符不能是數(shù)字(number);
  3. 變量的名稱(name)不能使用$符號。

shell 之所以強大,其中的一個因素是它可以在命令行中對變量作 替換(substitution)處理。 在命令行中使用者可以使用$符號加上變量名稱(除了用=定義變量名稱之外), 將變量值給替換出來,然后再重新組建命令行。

A=BCD
A=${A}E

上例中,我們使用{}將變量名稱范圍給明確定義出來, 如此一來, 我們就可以將A的變量值從BCD給擴充為BCDE。

嚴格來說,我們在當前shell中所定義的變量,均屬于 "本地變量"(local variable), 只有經過export命令的 "輸出"處理,才能成為"環(huán)境變量"(environment variable):

$ A=B
$ export A
或者$ export A=B

經過export輸出處理之后,變量A就能成為一個環(huán)境變量 供其后的命令使用。

exec 跟 source 差在哪?

了解一下進程 (process) 的概念
首先,我們所執(zhí)行的任何程序,都是父進程 (parent process) 產生的一個 子進程 (child process), 子進程在結束后,將返回到父進程去。 此現(xiàn)象在 Linux 中被稱為fork。當子進程被產生的時候,將會從父進程那里獲得一定的資源分配、及 (更重要的是) 繼承父進程的環(huán)境。

所謂環(huán)境變量其實就是那些會傳給子進程的變量。 簡單而言, "遺傳性" 就是區(qū)分本地變量與環(huán)境變量的決定性指標。 然而,從遺傳的角度來看,我們不難發(fā)現(xiàn)環(huán)境變量的另一個重要特征: 環(huán)境變量只能從父進程到子進程單向傳遞 換句話說:在子進程中環(huán)境如何變更,均不會影響父進程的環(huán)境。

所謂 shell script 講起來很簡單,就是將你平時在 shell prompt 輸入的多行 command line, 依序輸入到一個文件文件而已。

再結合以上兩個概念 (process + script),那應該不難理解如下的這句話的意思了: 正常來說,當我們執(zhí)行一個 shell script 時,其實是先產生一個 sub-shell 的子進程, 然后 sub-shell 再去產生命令行的子進程。

cd/etc/aa/bb/cc 可以執(zhí)行 但是把這條命令放入 shell 腳本后,shell 腳本不執(zhí)行!腳本運行完后并沒有換目錄 這是什么原因?
答案:因為,我們一般跑的 shell script 是用 sub-shell 去執(zhí)行的。 從 process 的概念來看,是 parent process 產生一個 child process 去執(zhí)行, 當 child 結束后,返回 parent, 但 parent 的環(huán)境是不會因 child 的改變而改變的。 所謂的環(huán)境變量元數(shù)很多,如 effective id(euid),variable, working dir 等等... 其中的 working dir(PWD)正是樓主的疑問所在:當用sub?shell來跑script的話,sub?shell的
pwd 會因為 cd 而變更
, 但返回 primary shell 時,$PWD 是不會變更的。

所謂source命令,就是讓 script 在當前 shell 內執(zhí)行、 而不是產生一個 sub-shell 來執(zhí)行。 由于所有執(zhí)行結果均在當前 shell 內執(zhí)行、而不是產生一個 sub-shell 來執(zhí)行。
$ source ./my_script.sh

() 與 {} 差在哪?

"命令群組"(command group) 的概念:將許多命令集中處理。
在 shell command line中,()與 {}這兩個符號都可以將多個命令當作群組處理, 技術細節(jié)上的差異:
()command group置于sub-shell(子shell) 中去執(zhí)行,也稱 nested sub-shell。
{} 則是在同一個shell內完成,也稱non-named command group。

要是在 command group中扯上變量及其他環(huán)境的修改, 我們可以根據(jù)不同的需求來使用(){}。 通常而言, 若所作的修改是臨時的,且不想影響原有或以后的設定, 那我們就使用(); 反之,則用{}。

(())與()還有${}差在哪?

$()(反引號)都可以用來做命令替換(command substitution)的,完成 或者$()里面的 命令,將其結果替換出來,再重組命令行。
我更喜歡用$()
先將command3替換出來給command2處理, 然后再將command2的處理結果,給command1來處理。

 command1 $(commmand2 $(command3)) 
 command1 `command2 \`command3\` ` #中間用 \ 轉義 `

不過,()并不是沒有弊端的...首先,``基本上可用在所有的unixshell中使用,若寫成shellscript,其移植性比較高。而()并不是每一種shell都能使用,我只能說, 若你用bash2的話,肯定沒問題... _

#是去掉左邊(在鍵盤上#在$的左邊)

%是去掉右邊(在鍵盤上%在$的右邊)

單個符號是最小匹配

兩個符號是最大匹配

定義一個變量file為:
file=/dir1/dir2/dir3/my.file.txt
我們可以用${}分別替換獲得不同的值:

${file#*/}  #拿掉第一個/及其左邊的字符串,其值為:dir1/dir2/dir3/my.file.txt
${file#*.}  #拿掉第一個.及其左邊的字符串,其值為:file.txt
${file##*/} #其值為:my.file.txt # 拿掉最后一個/及其左邊的字符串,其結果為: my.file.txt
${file##*.} #其值為:txt # 拿掉最后一個.及其左邊的字符串,其結果為: txt
${file%/*}  #其值為:/dir1/dir2/dir3 # 拿掉最后一個/及其右邊的字符串,其結果為: /dir1/dir2/dir3。
${file%.*}  #其值為:/dir1/dir2/dir3/my.file # 拿掉最后一個.及其右邊的字符串,其結果為: /dir1/dir2/dir3/my.file。
${file%%/*}  #其值為:其值為空。 # 拿掉第一個/及其右邊的字符串,其結果為: 空串。
${file%%.*}  #其值為:/dir1/dir2/dir3/my。# 拿掉第一個.及其右邊的字符串,其結果為: /dir1/dir2/dir3/my。

shell 字符串取子串

${file:0:5} #提取最左邊的5個字符:/dir1
 ${file:5:5} #提取第5個字符及其右邊的5個字符:/dir2

shell 字符串取子串的格式:${s:pos:length}, 取字符串 s 的子串:從 pos 位置開始的字符(包括該字符)的長度為 length 的的子串; 其中pos為子串的首字符,在 s 中位置; length為子串的長度。

shell 字符串變量值的替換

${file/dir/path}  #將第一個dir替換為path:/path1/dir2/dir3/my.file.txt
${file//dir/path} #將全部的dir替換為path:/path1/path2/path3/my.file.txt

shell 字符串變量值的替換格式:

  1. 首次替換: ${s/src_pattern/dst_pattern} 將字符串s中的第一個src_pattern替換為dst_pattern。
  2. 全部替換: ${s//src_pattern/dst_pattern} 將字符串s中的所有出現(xiàn)的src_pattern替換為dst_pattern.

計算 shell 字符串變量的長度:${#var}

${#file} #其值為27, 因為/dir1/dir2/dir3/my.file.txt剛好為27個字符

數(shù)組(array)的處理方法
一般而言, A="a b c def" 這樣的變量只是將
$A替換為一個字符串, 但是改為 A=(a b c def), 則是將$A定義為數(shù)組

${A[@]} #方法一
${A[*]} #方法二

以上兩種方法均可以得到:a b c def, 即數(shù)組的全部元素。

訪問數(shù)組的成員
${A[0]}
得到數(shù)組A的第一個元素

數(shù)組的 length

${#A[@]} #方法一,前面加個#
${#A[*]} #方法二

數(shù)組元素的重新賦值
A[3]=xyz #將數(shù)組A的第四個元素重新定義為 xyz

$(())是用來作整數(shù)運算的

在bash中, $(())的整數(shù)運算符號大致有這些:

  1. +- * / #分別為"加、減、乘、除"。
  2. % #余數(shù)運算,(模數(shù)運算)
  3. & | ^ ! #分別為"AND、OR、XOR、NOT"運算。
$ a=5; b=7; c=2;
$ echo $(( a + b * c ))
19
$ echo $(( (a + b)/c ))
6
$ echo $(( (a * b) % c ))
1

&& 與 || 差在哪?

&&|| 都是用來 "組建" 多個 command line 用的;
command1 && command2 # command2 只有在 command1 的 RV 為 0(true) 的條件下執(zhí)行。
command1 || command2 # command2 只有在 command1 的 RV 為非 0(false) 的條件下執(zhí)行。

[ -n string ]是測試 string 長度大于 0, 則為 true。
當 $A 被賦值時,在看看其是否小于 100,否則輸出 too big!

$ A=123
$ [ -n "$A" ] && [ "$A" -lt 100 ] || echo 'too big!'
$ too big!

若取消 A,照理說,應該不會輸出文字啊,(因為第一個條件不成立)。

$ unset A
$ [ -n "$A" ] && [ "$A" -lt 100 ] || echo 'too big!'
$ too big!

但還是可以得到上面的結果, 解決方法如下
方法1 sub-shell

$ unset A
$ [ -n "$A" ] && ( [ "$A" -lt 100 ] || echo 'too big!' )  #利用 ()

方法2 command group

$ unset A
$ [ -n "$A" ] && { [ "$A" -lt 100 ] || echo 'too big!'}  #利用 {}

你要 if 還是 case 呢?

cmd1 && {
    cmd2
    cmd3
    ;
} || {
    cmd4
    cmd5
}

若 cmd1的return value為true的話, 然后執(zhí)行cmd2與cmd3, 否則執(zhí)行cmd4與cmd5.

與上等效


if cmd1
then
    cmd2
    cmd3
else
    cmd4
    cmd5
fi

只要if后面的command line返回true的return value (我們常用test命令返回的return value), 然則就執(zhí)行then后面的命令,否則,執(zhí)行else之后的命令, fi則是用來結束判斷式的keyword。

if cmd1; then
    cmd2;
elif cmd3; then
    cmd4
else
    cmd5
fi

若cmd1為true,然則執(zhí)行cmd2; 否則在測試cmd3,若為true則執(zhí)行cmd4; 倘若cmd1與cmd3均不成立,那就執(zhí)行cmd5

for what? while 與 until 差在哪?

for loop
for loop 是從一個清單列表中讀進變量的值, 并依次的循環(huán)執(zhí)行do到done之間的命令行。 例:

for var in one two three four five
do
    echo -----------------
    echo '$var is '$var
    echo
done

上例的執(zhí)行結果將會是:

  1. for會定義一個叫var的變量,其值依次是one two three four five。
  2. 因為有5個變量值,因此,do與done之間的命令行會被循環(huán)執(zhí)行5次。
  3. 每次循環(huán)均用echo產生3個句子。而第二行中不在hard quote之內的$var會被替換。
  4. 當最后一個變量值處理完畢,循環(huán)結束。
    在for loop中,變量值的多寡,決定循環(huán)的次數(shù)
    對于一些“累計變化”的項目(整數(shù)的加減),for 也能處理:
for ((i = 1; i <= 10; i++))
do
    echo "num is $i"
done

while loop
除了for loop, 上面的例子, 我們也可改用while loop來做到:

num=1
while [ "$num" -le 10 ]; do
    echo "num is $num"
    num=$(($num + 1))
done

分析上例:

  1. 在while之前,定義變量num=1.
  2. 然后測試(test)$num是否小于或等于10.
  3. 結果為true,于是執(zhí)行echo并將num的值加1.
  4. 再作第二輪測試,此時num的值為1+1=2,依然小于或等于10,因此,為true,循環(huán)繼續(xù)。
  5. 直到num為10+1=11時,測試才會失敗...于是結束循環(huán)。

while loop的原理與for loop稍有不同: 它不是逐次處理清單中的變量值, 而是取決于while 后面的命令行的return value:

  1. 若為true, 則執(zhí)行do與done之間的命令, 然后重新判斷while后的return value。
  2. 若為false,則不再執(zhí)行do與done之間的命令而結束循環(huán)。

until loop
until loop與while相反, until是在return value 為false時進入循環(huán),否則,結束。 因此,前面的例子也可以輕松的用until來寫:

num=1
until [ ! "$num" -le 10 ]; do
    echo "num is $num"
    num=$(($num + 1))
done
或者
num=1
until [ "$num" -gt 10 ]; do
    echo "num is $num"
    num=$(($num + 1))
done

if [ 1 -ne 1 ];then
...
fi
-eq:等于
-ne:不等于
-le:小于等于
-ge:大于等于
-lt:小于
-gt:大于

shell loop 中的 break 與 continue

break用來中斷循環(huán),也就是強迫結束循環(huán)。
continue則與break相反:強迫進入下一次循環(huán)動作
可簡單的看成: 在continuedone之間的句子略過而返回到循環(huán)的頂端

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容