Linux的PS1.PS2.PS3.PS4等環境變量;Crontab的兩個坑人點;變量傳遞等

1.問題出現:

我爲了實現一個功能,就是讓PS1變量(命令行提示符)每隔1分鐘(利用crontab計劃任務)變化一次顏色和背景格式以實現酷炫的效果,可是通過了各類嘗試均以失敗了結。雖然可以實現讓PS1每按一次回車變化一次顏色(這個有人想嘗試的話下面寫的有),可是沒法作到讓它每隔一段時間進行一次格式的變化
爲了解決這個問題,進行了一些研究,總結了一下寫在下面linux

附加:PS1每按一次回車實現顏色變化實現:

  1. 先在腳本中寫入:shell

    #!/bin/bash
    PS1="\033[01;\$[RANDOM%7+31]m\A[\u@\h \w]\\$\033[0m "
  2. 而後命令行直接source 此腳本便可(或者把它放到/etc/profile.d文件夾下,每次開機開啓shell後就會自動執行)

注意點:centos

  • 這裏固然也能夠直接在命令行中輸入腳本中所寫的那個命令,不過這樣的話關閉shell後下次就會丟失
  • PS1的值不能用RANDOM計算過以後的值,否則它只能在source的時候執行一次並取了一次RANDOM的值,以後就固定了,而這裏所寫的PS1利用了\$[RANDOM]把它給註釋掉了,所以此時查看PS1可知:
11:01[root@centos7 /data/scriptest]# echo $PS1
\033[01;$[RANDOM%7+31]m\A[\u@\h \w]\$\033[0m
  • 所以PS1每按一次回車就會從新執行上面所寫的變量賦值(顯示出PS1命令提示符),因此能夠實現每次按回車變換顏色的功能。
  • 由此也能夠得出在linux中命令提示符的值是每按一次回車就會根據PS1變量中所寫的內容進行輸出並顯示在屏幕上的,而並不是讀入內存以後就一成不變了

2.如下是問題的分析和總結:

環境變量

  1. 咱們知道,環境變量(全局變量)雖然可以在當前shell以及它的子shell中使用,但在子shell中僅僅是調用它而已,雖然能繼承並使用這個變量的值,可是這個子shell並不能改變它所調用的環境變量的值並傳遞給父shell中。注意這和函數不一樣,看下面的例子:
20:59[root@centos7 /data/scriptest]# declare -x aaa=12345
20:59[root@centos7 /data/scriptest]# echo $aaa
12345
20:59[root@centos7 /data/scriptest]# ./testsource2.sh
12345
123123
20:59[root@centos7 /data/scriptest]# echo $aaa
12345
  1. 而定義一個函數則它即是在當前shell中運行的,並未開啓子shell,所以若不用local命令定義局部變量,則環境變量會被函數給改變。
    • 這裏在命令行中定義函數的時候注意中括號中每一個命令後面都要加上分號,且前面的中括號要和命令之間有空格,後面的沒要求。

沒定義localbash

21:01[root@centos7 /data/scriptest]# echo $aaa
12345
21:01[root@centos7 /data/scriptest]# funsor() { aaa=555 ; return 0 ; } 
21:02[root@centos7 /data/scriptest]# echo $aaa
12345
21:02[root@centos7 /data/scriptest]# funsor
21:02[root@centos7 /data/scriptest]# echo $aaa
555

定義localapp

21:08[root@centos7 /data/scriptest]# echo $aaa
12345
21:08[root@centos7 /data/scriptest]# funsor2() { local aaa=555 ; echo $aaa ; return 0 ; } 
21:08[root@centos7 /data/scriptest]# funsor2
555
21:08[root@centos7 /data/scriptest]# echo $aaa
12345

特別注意點(目前測試過的,下面有測試過程A和B):

  1. 本身在shell開啓後的命令行中或者說在開啓shell的時候載入的配置文件中定義的環境變量,只要不是下面中所說的極特殊的那些(不能被直接繼承的),都可以在當前shell中開啓的子shell中被繼承。
  2. 系統(shell自動配置的)中經常使用的環境變量,在linux的bash shell中,若是再次開啓子bash shell,按照分析得知應該能所有被繼承(由於環境變量的特性),但測試得知並不是徹底如此:

而可以被這個子shell直接繼承的有(基本上在開機後shell開啓後用declare -x命令查看到的這些出現的變量都可以繼承)ide

  1. PATH變量
  2. PATH
  3. PWD
  4. HOSTNAME
  5. HISTSIZE
  6. HISTCONTROL
  7. PS3:
    • 它是命令select後選擇時出現的選擇提示符,默認是沒有定義的空字符,且默認不是環境變量,此時會顯示#?。
    • 通過測試把它定義爲環境變量並賦值以後,在子shell中可以直接繼承父shell的PS3

不可以被直接繼承的有函數

  1. PS1:測試

    • 它就是命令行的提示符的值,能夠有不少格式,具體查看幫助man bash.
    • 它默認不是一個環境變量,可是就算用命令declare -x把它定義爲了環境變量,子shell也沒法繼承,子shell中通過測試爲空;
    • 因而可知這個PS1應該是一個特殊變量,這也側面解釋了bash shell開啓的時候默認沒把它定義爲環境變量,下面幾個PS同理
  2. PS2:centos7

    • 同上,默認不是環境變量,定義爲環境變量以後也沒法繼承;
    • 它是一個很是長的命令能夠經過在末尾加「\」使其分行顯示後的多行命令的默認提示符,默認已經定義爲普通變量,值爲"> "
  3. PS4:命令行

    • 同上
    • 它就是set -x命令用來修改跟蹤輸出的前綴,默認定義爲普通變量且值爲"+ "
  4. 更多的其餘還沒測試,之後補充,不過從1中可見有些特殊變量就算定義爲了全局變量,在子shell開啓的時候也會把它覆蓋掉從而沒法繼承(至關於在shell開啓過程是中從新聲明定義了這些變量,這個就是開啓shell時的內部邏輯了)
    • 注意,以上說的將PS定義爲環境變量都是開啓shell時以後的操做,只是存入內存中了,若是另開新的shell,這些操做都會失效,恢復到默認的shell設置
    • 同時咱們能夠想到,只要能找到定義PS1,PS2,PS3,PS4的配置文件位置(shell開啓時),並將它修改成本身想要的值,(就好比上面的PS1每次都改變的命令),這樣每次開啓一個新的shell,就算這些環境變量不能繼承,可是按照shell開啓時載入的配置文件中寫的這些特殊的環境變量默認設置,就能部分實現本身想要的設置。不過更方便的方法仍是直接在子shell中source一個配置文件便可,這個配置文件中寫上這些環境變量的賦值便可,惟一的缺憾就是不能修改後傳遞給父shell(這部分不理解先把下面的分析看完再回頭看)。

測試過程A:PS的繼承變量的測試(可先把下面的分析看完再回頭看):

  1. 首先新開一個終端(也就是新開shell)測試
    文件(腳本)中所寫:
    PStest
#!/bin/bash   
echo PS1=$PS1
echo PS2=$PS2
echo PS3=$PS3
echo PS4=$PS4
select i in test1 test2 test3; do                                                                                                                     
        case $i in
        *)
           echo $i
           break
           ;;
        esac
done
  1. 在當前shell中source這個文件結果:
    能夠看到默認的PS變量和select提示符:
12:10[root@centos7 /data/scriptest]# . PStest 
PS1=\[\033[01;35m\]\A[\u@\h \w]\$\[\033[00m\]
PS2=>
PS3=
PS4=+
1) test1
2) test2
3) test3
#? 2
test2
12:10[root@centos7 /data/scriptest]#
  1. 在當前shell中把PS變量都定義爲環境變量:
12:15[root@centos7 /data/scriptest]# declare -x PS1 PS2 PS3 PS4
12:15[root@centos7 /data/scriptest]# declare -x :   查看
declare -x PS1="\\[\\033[01;35m\\]\\A[\\u@\\h \\w]\\\$\\[\\033[00m\\] "
declare -x PS2="> "
declare -x PS3
declare -x PS4="+ "
  1. 先在子shell中直接測試,也就是直接執行此文件以腳本方式:
    可見就算定義爲環境變量,PS1和PS2也沒有繼承,子shell中爲空。目前還沒法判斷PS3和PS4
12:15[root@centos7 /data/scriptest]# PStest 
PS1=
PS2=
PS3=
PS4=+
1) test1
2) test2
3) test3
#? 1
test1
12:17[root@centos7 /data/scriptest]#
  1. 而後在當前shell中修改PS3,PS4的值(上一步已經知道PS1,PS2沒法繼承了):
12:21[root@centos7 /data/scriptest]# PS3="Please input"
12:22[root@centos7 /data/scriptest]# PS4="=== "
12:22[root@centos7 /data/scriptest]# declare -x
declare -x PS1="\\[\\033[01;35m\\]\\A[\\u@\\h \\w]\\\$\\[\\033[00m\\] "
declare -x PS2="> "
declare -x PS3="Please input"
declare -x PS4="=== "
  1. 最後再次以腳本方式也就是子shell方式執行此文件(腳本):
    可見PS3能夠直接繼承,而且在select中生效了,而PS4並無繼承,仍是原先的值。
12:22[root@centos7 /data/scriptest]# PS1test.sh 
12:24[root@centos7 /data/scriptest]# PStest 
PS1=
PS2=
PS3=Please input
PS4=+
1) test1
2) test2
3) test3
Please input3
test3

測試過程B:下面是測試能夠被子shell直接繼承的由shell自己默認定義的變量的一些測試過程:

先寫腳本,而後以子shell方式進行測試:

21:36[root@centos7 /data/scriptest]# cat testsource.sh -n
     1  #!/bin/bash
     2  echo PATH=$PATH
     3  echo PWD=$PWD
     4  echo HOSTNAME=$HOSTNAME
     5  echo HISTSIZE=$HISTSIZE
     6  echo HISTCONTROL=$HISTCONTROL
     7  
21:36[root@centos7 /data/scriptest]# ./testsource.sh 
PATH=/data/app/httpdnew/bin:/data/app/cmatrix/bin:/data/app/tree/bin:/data/scriptest/:.:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin:/root/bin
PWD=/data/scriptest
HOSTNAME=centos7.6test
HISTSIZE=1000
HISTCONTROL=ignoreboth

crontab的兩個坑人注意點:%和環境變量

crontab的執行過程

它的執行過程比較特殊,它執行的時候並不會從當前shell中繼承各類系統定義的環境變量和本身定義的環境變量(全局變量)等等,所以必須在它執行的時候傳遞給它各類環境變量才能保證後的命令徹底正確的執行。分狀況分析:

  1. 系統定義的環境變量等等,這些環境變量在開機(開啓shell)的時候會載入配置文件從而在當前shell中獲得數值,而這些環境變量在crontab中基本上都不會繼承。
  2. 本身定義的一些普通變量,好比說本身在/etc/profile.d文件夾中或者/etc/profile, ~/.bashrc等這些配置文件中定義的普通變量,再開機(開啓shell後)並已經載入內存中,這些統統不會繼承,包括直接在命令中定義的普通變量(這些在bash shell中開啓子shell都不會繼承,更別說在crontab中了)
  3. 本身定義的一些環境變量,包括2中說的這些文件中的,或者在命令行中直接定義的環境變量,也不可以在crontab中繼承
  • 注意,本身在命令行中定義的變量直接就存入了內存中,下次開啓shell就會丟失,而文件中的下次開啓shell不會丟失。但須要區分環境變量和普通變量。

從上面可見crontab幾乎不會繼承任何變量,不管是系統定義的仍是本身定義的,不管是環境仍是普通變量,不管是內存中的仍是文件中的。
它也是開啓了一個子shell,不過與bash shell的區別就在於環境變量不會繼承。所以爲了命令的正確進行,可有下面的比較推薦的兩種解決方式:

  1. 直接在crontab -e中的執行頻率後面,真正要執行的命令前面,寫入引用命令: source /etc/profile && source ~/.bash_profile (這裏沒有寫上所有的環境變量配置文件)
    • 這個命令就是爲了把這些配置文件(包括本身定義的環境變量文件)引用進crontab執行環境中去,也就是把這些環境變量先導入,而後再執行須要執行的命令
  2. 若是crontab中的命令是要執行腳本,則在後面須要執行的腳本中添加寫入1中的source引用,而後就可使用這些環境變量了

須要注意點:

  1. crontab 不管如何操做都引用不了本身在當前shell的命令行中直接用命令declare -x(或export)定義的環境變量,由於它們在內存中,沒法引用出來。(注意和shell的區別,當前shell中開啓的子腳本(子shell)中能夠引用它們,前面已經分析過了)
  2. crontab 在書寫命令的時候最好要加上全局路徑,由於PATH這個變量默認也是沒有引用的,不過PATH這個變量其實默認在/etc/crontab中定義過,crontab是按照這裏面的定義來判斷PATH變量的值的,而不是從當前的shell中繼承。
    • 其實這個文件是能夠定義一些環境變量的,好比把PATH等等寫進去和當前shell中的PATH相同,這樣的話crontab執行命令的時候就不用在寫全路徑了
    • 可參照下面本來的格式(上面定義環境變量的部分)來寫,提早定義一些環境變量。不過推薦仍是按照上面的兩種方式來解決,由於若是環境變量變化了每次都要修改這個文件:
21:37[root@centos7 /data/scriptest]# cat /etc/crontab 
//就是按照下面這3行的格式來定義本身須要的環境變量
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed

source的簡單說明

source的命令其實很簡單,就至關因而在當前的shell中執行文件中的命令(把文件中的每一行命令拉到命令行來執行),相似於函數,所以它可以改變當前shell的環境變量等等。
這也是爲什麼咱們用source來進行配置文件(尤爲是環境變量)的修改以後讓它生效的,而不是用 「bash 腳本」 或者添加PATH和執行權限後直接執行腳本的方式來修改環境變量。
由於後兩種方式修改的環境變量只能在子腳本(shell)中有效,而前面說過雖然子腳本能繼承環境變量(除了那些特殊的好比PS1,就算父shell修改PS1定義爲環境變量,當開啓子shell後它在子shell中也默認爲空值沒有定義),可是修改這些環境變量的值並不能返回到父shell中,也就實現不了使配置文件生效的目的了(其實生效了,不過是在子shell中生效的,子shell一旦退出全部配置便消失不能傳給父shell)

3.問題的結論:

從上面分析得知,不論怎樣都沒法在子shell中修改環境變量(包括PS1)的值並傳給父shell,而crontab默認開啓子shell,所以它不只改不了PS1,其餘的環境變量也沒法應用到父shell中,就算用source命令也只是在crontab開啓的子shell中應用這些環境變量,不能修改它們傳遞到父shell也就是當前shell中。

  • 同時,咱們可知在使用crontab的過程當中,不能寫入修改當前shell中任何變量(普通,環境)的命令,就就算寫了,這些命令也都是無效的沒法傳回當前shell從而修改這些變量的值。

更多分析:

  • 只有一種方法,也就是用crontab修改文件的內容(文件裏能夠寫入環境變量),由於文件是保存在磁盤中的,每次使用它的時候纔會讀入內存,這就和shell無關了,也就至關於全部的shell均可以查看並使用這些文件,實現了曲線傳遞數據的方法。
  • 而後退出以後在父shell也就是當前shell中執行一下source命令這些文件,這樣纔可以實如今當前shell中實現環境變量的更新。不過這樣作還得手動source一下,至關於沒法自動配置了,crontab也就沒有意義。
  • 不過用這種方式用來配置系統服務和一些其餘程序的配置文件,仍是可以生效的(配置完以後別忘了從新讀入配置文件或者重啓),固然也能進行一些備份操做等等(由於備份就是存入文件到磁盤中,和shell無關)
  • 緣由就是由於(這些程序若是配置的時候須要環境變量,就按照上面的解決方法來導入環境變量)在crontab修改它們的配置文件後從新載入或者重啓,退出crontab以後這些服務並不會關閉,而此時配置文件已經讀入內存,因此也就實現了shell之間的服務程序的配置傳遞,(若是有必要還能夠再加上nohup命令讓它和終端也無關)

crontab的%的說明

這個在crontab中表明換行,想要使用它要麼\%轉義的方式,要麼就把它寫入腳本中,或者寫在單引號中不須要轉義,不過此時就不能用於計算取餘或者字符串變量操做中的一些命令了。
可是注意別忘了%它不能在crontab中直接使用

  • 更多關於crontab的使用可看計劃任務博客一章,比較經常使用的一些:tail /var/log/cron :查看cron執行日誌cat /var/spool/cron/用戶名:相似於crontab -l ,查看用戶名的用戶定義的crontab命令。
相關文章
相關標籤/搜索