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命令。
相关文章
相关标签/搜索