一、命令行参数

1.命令行构成

Linux 命令通常由命令名称、选项、参数三部分构成,以下是具体介绍:

命令名称

这是命令的核心部分,用于指定要执行的操作或功能,通常是一个具有特定含义的单词或缩写。例如,ls命令用于列出目录内容,cd命令用于更改当前目录,cp命令用于复制文件或目录等。命令名称是必须的,它告诉系统要执行什么任务。

选项

选项通常用于修改命令的行为或提供额外的功能,它们以短横线-(短选项)或双短横线--开头(长选项),后面跟着一个或多个字符。例如,ls -l中的-l选项表示以长格式列出目录内容,显示更多详细信息;ls --all中的--all选项表示列出所有文件,包括隐藏文件。有些命令支持多个选项,可以组合使用,如ls -al。

参数

参数是命令操作的对象或目标,它可以是文件名、目录名、设备名、进程 ID 等。例如,cp file1.txt file2.txt中,file1.txt是源文件,file2.txt是目标文件,它们都是cp命令的参数;cd /home/user中,/home/user是要切换到的目标目录,是cd命令的参数。有些命令可能需要多个参数,有些命令可能不需要参数,具体取决于命令的功能和用途。

2.main函数参数

我们熟悉的main函数,其实也是有参数的:

int argc:参数的个数char *argv[]:参数的清单,以NULL结尾char *envp[]:环境变量,以NULL结尾

#include

#include

#include

using namespace std;

int main(int argc,char* argv[],char* envp[])

{

return 0;

}

我们先认识前两个变量:argc和argv,通过打印来看现象。

argc记录包括运行程序在内的参数个数,argv以字符串的形式记录包括运行程序在内的参数。

为什么要有命令行参数列表?

命令的选项是由命令行的参数实现的,同一个程序,可以根据命令行参数,根据选项不同,表现出不同的功能,比如:指令中选项的实现。

例如:

main函数的参数是怎么传递的?

当程序从命令行启动时,操作系统(shell进程)负责解析命令行输入。操作系统会根据空格等分隔符将命令行输入分割成不同的字符串,这些字符串就是各个参数。然后,操作系统会将参数的数量赋值给argc,并将指向这些字符串的指针存储在argv数组中。接着,操作系统调用main函数,并将argc和argv作为参数传递给它,这样main函数就可以通过argc和argv来获取命令行参数的信息。

当main函数创建子进程时,子进程通常会继承父进程(即main函数所在进程)的一些环境信息,包括argc和argv所包含的命令行参数信息。这是因为子进程在创建时会复制父进程的地址空间等资源(写实拷贝),所以子进程可以访问到与父进程相同的命令行参数。

二、环境变量

1.概念

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的参数;如在编写代码时,在链接阶段,我们并不知道所链接的东静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找;环境变量通常具有全局特性。

前面我们提到main函数的第三个参数char *envp[]就是环境变量表,以NULL结尾;

所有环境变量的格式为:key=value;

查看环境变量:

1、利用main函数的envp参数;

2、命令:env,显示shell的环境变量表;

ubuntu@VM-8-11-ubuntu:~$ env

SHELL=/bin/bash

PWD=/home/ubuntu

LOGNAME=ubuntu

XDG_SESSION_TYPE=tty

HOME=/home/ubuntu

LANG=en_US.UTF-8

LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=00:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.avif=01;35:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:*~=00;90:*#=00;90:*.bak=00;90:*.crdownload=00;90:*.dpkg-dist=00;90:*.dpkg-new=00;90:*.dpkg-old=00;90:*.dpkg-tmp=00;90:*.old=00;90:*.orig=00;90:*.part=00;90:*.rej=00;90:*.rpmnew=00;90:*.rpmorig=00;90:*.rpmsave=00;90:*.swp=00;90:*.tmp=00;90:*.ucf-dist=00;90:*.ucf-new=00;90:*.ucf-old=00;90:

PROMPT_COMMAND=history -a;

SSH_CONNECTION=120.229.225.20 24848 10.1.8.11 22

LESSCLOSE=/usr/bin/lesspipe %s %s

XDG_SESSION_CLASS=user

TERM=xterm

LESSOPEN=| /usr/bin/lesspipe %s

USER=ubuntu

DISPLAY=localhost:10.0

SHLVL=1

XDG_SESSION_ID=35633

XDG_RUNTIME_DIR=/run/user/1000

SSH_CLIENT=120.229.225.20 24848 22

DEBUGINFOD_URLS=https://debuginfod.ubuntu.com

XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

SSH_TTY=/dev/pts/1

_=/usr/bin/env

2.常见环境变量:PATH

PATH:指定命令的搜索路径;PATH是一个路径集合,用:符号分隔路径的路径集合,使用命令时shell会按照顺序在PATH的路径中从左向右查找命令,以查到的第一个路径下对应的命令为止。

ubuntu@VM-8-11-ubuntu:~$ echo $PATH

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

以前我们编写的c++程序,例如proc.cpp,通过g++ proc.cpp -o proc,形成可执行程序proc,如果需要运行需要指明proc的绝对路径或者相对路径,例如:./proc来运行;

我们知道命令也是程序,为什么命令就不需要指明路径,例如:ls、pwd;那是因为这些程序在目录/usr/bin下,所以可以不指明路径运行;如果将proc添加在/usr/bin目录下,也同样可以不指明路劲直接运行。

那为什么在/usr/bin目录下的程序就可以不指明路劲呢?

是因为环境变量PATH告诉了shell去PATH指定的路径下搜索命令。

如果我想将自己的程序可以不带路径直接运行,有两种做法:

将可执行程序添加到PATH下的任意一个路径,比如:将proc拷贝到/usr/bin目录下将可执行程序所在的路径添加到PATH中,比如:将/home/ubuntu添加到PATH中

命令:PATH=$PATH:/home/ubuntu

ubuntu@VM-8-11-ubuntu:~$ echo $PATH

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

ubuntu@VM-8-11-ubuntu:~$ PATH=$PATH:/home/ubuntu

ubuntu@VM-8-11-ubuntu:~$ echo $PATH

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/ubuntu

注意: 环境变量是内存级别的,存在于一个shell进程里,即启动一个shell进程后,shell进程会读取用户和系统相关的环境变量的配置文件(这也说明了环境变量的数据从配置文件读取的),形成自己的环境变量表(相当于shell进程内的变量去拷贝配置文件,形成shell进程的上下文),退出shell进程再重新启动shell进程后又重新读取配置文件,所以当修改环境变量时,因为是内存级别的数据,当退出shell进程又重新启动shell进程,对环境变量的修改就会不见。 如果要使修改永久有效,就要修改系统的配置文件,因为shell进程每次启动都会读取配置文件。

关于环境变量的配置文件:

每个用户的家目录下,存在.bashrc和.bash_profile配置文件,当shell进程启动时就会读取这两个配置文件形成环境变量信息。

1. .bashrc

适用范围:主要用于 Bash shell,是用户专属的交互式 shell 配置文件。每次打开一个新的交互式 Bash shell 时,这个文件都会被读取。作用:可用于设置别名、环境变量、函数,以及定义命令提示符样式等。

2. .bash_profile 或 .profile

适用范围:同样用于 Bash shell。.bash_profile 在登录时被读取,而 .profile 是一个更通用的配置文件,一些非 Bash shell 可能会读取它。如果 .bash_profile 存在,.profile 通常不会被读取。作用:一般用于执行一些登录时需要初始化的操作,如设置环境变量、执行脚本等。通常会在其中调用 .bashrc 文件。

3.常见环境变量--USER

前面说到进程内部都会用UID记录是谁启动的这个进程,那么在启动进程的时候,系统怎么知道你是谁,并且把你的UID写入到进程的PCB中?-- 环境变量USER

USER:当前登录的用户名

root@VM-8-11-ubuntu:~# echo $USER

root

4.常见环境变量--HOME

HOME:当前用户家目录

ubuntu@VM-8-11-ubuntu:~$ echo $HOME

/home/ubuntu

我们知道,bash也是一个进程,登录bash时,会默认处在家目录里,也就是说bash进程的cwd(工作目录)此时就是指向家目录,那为什么cwd会起初指向家目录呢?(为什么登录bash会默认在家目录里?)-- 在用户登录时,系统会根据 /etc/passwd 文件中的信息设置 HOME 环境变量,这个环境变量存储了用户的家目录路径。然后,在启动 Bash 时,系统会调用相关的系统调用(如 chdir())将当前工作目录cwd切换到 HOME 环境变量所指向的路径。

一般情况下 bash 创建的子进程会继承bash的 cwd,但子进程有能力主动修改自己的 cwd,或者在某些特殊启动方式下被指定不同的工作目录。

5.常见环境变量--SHELL

SHELL:记录用户登录时启动的SHELL版本

ubuntu@VM-8-11-ubuntu:~$ echo $SHELL

/bin/bash

6.常见环境变量--PWD、OLDPWD

PWD:表示当前工作目录(Present Working Directory)。当你在终端中切换目录时,PWD 的值会自动更新为当前所在的目录路径。

OLDPWD:最近一次的工作路径

ubuntu@VM-8-11-ubuntu:~$ pwd

/home/ubuntu

ubuntu@VM-8-11-ubuntu:~$ cd /

ubuntu@VM-8-11-ubuntu:/$ echo $PWD

/

ubuntu@VM-8-11-ubuntu:/$ echo $OLDPWD

/home/ubuntu

7.获取环境变量的方式--main函数参数、标准库函数getenv、第三方变量environ

main函数参数:前面已经示范过了

标准库函数函数getenv():

第三方变量:extern char **environ,指向环境变量表

8.标准库函数设置环境变量--putenv

putenv :用于设置环境变量的 C 语言标准库函数,putenv 函数的主要功能是将指定的环境变量添加到当前进程的环境列表中,或者修改已存在环境变量的值。如果指定的环境变量名不存在,则会创建该环境变量;如果已存在,则会更新其值。

参数:string 是一个指向以 '\0' 结尾的字符串指针,该字符串的格式必须为 name=value,其中 name 是环境变量名,value 是环境变量的值。返回值:如果函数调用成功,返回值为 0;如果调用失败,返回非零值。

#include

#include

int main() {

// 定义要设置的环境变量字符串

char *new_env = "MY_VARIABLE=Hello, World!";

// 调用 putenv 函数设置环境变量

if (putenv(new_env) != 0) {

perror("putenv");

return 1;

}

// 获取并打印设置后的环境变量值

char *value = getenv("MY_VARIABLE");

if (value != NULL) {

printf("MY_VARIABLE: %s\n", value);

} else {

printf("Failed to get MY_VARIABLE.\n");

}

return 0;

}

三、本地变量

在 Linux 系统中,本地变量(也称为局部变量)是在特定的环境或脚本中定义的变量,其作用域通常局限于定义它们的脚本、函数或代码块内。

1.本地变量定义和赋值

在 Linux 的 shell 脚本里,你可以使用以下语法来定义并赋值给本地变量:

variable_name=value

这里要注意,= 两边不能有空格。下面是一个简单的示例:

# 定义一个本地变量

message="Hello, World!"

# 打印变量的值

echo $message

在上述脚本中,message 是一个本地变量,它被赋值为 "Hello, World!",然后使用 echo 命令将其值输出。

但如果没有加双引号,直接message=Hello World!,shell 会把 Hello 当作变量赋值部分,而把 World 当作一个单独的命令,这样就会导致命令执行出错。使用双引号后,"Hello World" 会被完整地赋值给变量。

注意:

a=111 和 a="111" 在这个简单的赋值场景下功能基本一致,都能将 111 赋值给变量 a。双引号在赋值时可以用来保留字符串中的特殊字符,避免它们被 shell 进行错误解释,在处理包含空格、通配符等特殊字符的字符串时非常有用。

2.本地变量作用域

本地变量的作用域通常局限于定义它们的脚本或函数内部。一旦脚本或函数执行完毕,这些变量就会被销毁。

例如:脚本内的本地变量

# 定义一个本地变量

local_variable="This is a local variable"

echo $local_variable

# 在子shell中尝试访问该变量

(

echo $local_variable

)

# 在脚本结束后,该变量就不再存在

在这个例子中,local_variable 是在脚本中定义的本地变量,它可以在脚本内部被访问。当在子 shell(用括号 () 表示)中尝试访问该变量时,也可以正常获取其值。

例如:函数内的本地变量

#!/bin/bash

function print_message() {

# 定义一个函数内的本地变量

local function_variable="This is a local variable in a function"

echo $function_variable

}

# 调用函数

print_message

# 尝试在函数外部访问该变量

echo $function_variable

在这个脚本中,function_variable 是在 print_message 函数内部定义的本地变量,使用 local 关键字明确声明。该变量只能在函数内部访问,函数外部无法获取其值。(如果没有local声明,定义的变量属于脚本内的本地变量)

3.本地变量的引用

要引用本地变量的值,需要在变量名前加上 $ 符号。例如:

#!/bin/bash

name="John"

echo "My name is $name."

在上述代码中,$name 表示引用 name 变量的值。

4.本地变量的删除

可以使用 unset 命令来删除本地变量:

#!/bin/bash

age=25

echo $age

# 删除变量

unset age

echo $age

在这个脚本中,首先定义了 age 变量并输出其值,然后使用 unset 命令删除该变量,再次尝试输出该变量时,将不会有任何结果。

5.本地变量的查看

命令env只查看环境变量,命令set会显示所有变量(包括本地变量和环境变量)以及函数;declare 命令会列出所有变量(包括本地变量和环境变量)

6.本地变量导出变成环境变量

命令:export 本地变量名(i=100;export i 等价于export i=100),将本地变量导出变成环境变量(退出shell后这个导出的环境变量就不见了,环境变量是内存级的,这个导入的环境变量不在配置文件中)

7.环境变量和本地变量的区别

环境变量和本地变量都是shell维护的子进程,而环境变量可以被子进程继承(环境变量具有“全局”属性),本地变量不可以被子进程继承(本地变量具有“局部”属性)。