Info bash
Loginshell, subshell .bash_profile and .bashrc are specific to bash, whereas .profile is read by many shells in the absence of their own shell-specific config files. (.profile was used by the original Bourne shell.).bash_profile or .profile is read by login shells, along with .bashrc; subshells read only .bashrc. (Between job control and modern windowing systems, .bashrc by itself doesn’t get used much. If you use screen or tmux, screens/windows usually run subshells instead of login shells.) The idea behind this was that one-time setup was done by .profile (or shell-specific version thereof), and per-shell stuff by .bashrc. For example, you generally only want to load environment variables once per session instead of getting them whacked any time you launch a subshell within a session, whereas you always want your aliases (which aren’t propagated automatically like environment variables are). Other notable shell config files: /etc/bash_profile (fallback /etc/profile) is read before the user’s .profile for system-wide configuration, and likewise /etc/bashrc in subshells (no fallback for this one). Many systems including Ubuntu also use an /etc/profile.d directory containing shell scriptlets, which are .(source)-ed from /etc/profile; the fragments here are per-shell, with *.sh applying to all Bourne/POSIX compatible shells and other extensions applying to that particular shell.
bash -c sudo bash -c “ls > test” Copy without hidden files: rsync -av –exclude=”.*” src dest https://stackoverflow.com/questions/11557114/cp-r-without-hidden-files
Bash scripting is scripting specifically for Bash
Shell scripting is scripting in any shell
${parameter//find/replace}
INSTALL_TARGET_SERVERs='IP1,IP2'
for i in ${INSTALL_TARGET_SERVERs//,/ }; do echo "$i"; done
for i in ${INSTALL_TARGET_SERVERs//,/$IFS}; do echo "$i"; done
$0、$1、$2、$#、$@、$*、$?
./test.sh a b
$0 对应 ./test.sh 这个值。如果执行的是 ./work/test.sh, 则对应 ./work/test.sh 这个值
$1 a
$2 b
${10} 表示获取第 10 个参数的值 $10 相当于 ${1}0,也就是先获取 $1 的值,后面再跟上 0
$# 2 对应传入脚本的参数个数,统计的参数不包括 $0
$@ 会获取到 "a" "b" ,也就是所有参数的列表,不包括 $0。
$* a b 把所有参数合并成一个字符串
$? 可以获取到执行 ./test.sh a b 命令后的返回值。在执行一个前台命令后,可以立即用 $? 获取到该命令的返回值:
当执行系统自身的命令时,$? 对应这个命令的返回值
当执行 shell 脚本时,$? 对应该脚本调用 exit 命令返回的值。如果没有主动调用 exit 命令,默认返回为 0。
当执行自定义的 bash 函数时,$? 对应该函数调用 return 命令返回的值。如果没有主动调用 return 命令,默认返回为 0。
The default value of IFS is a three-character string comprising a space, tab, and newline:
$ echo "$IFS" | cat -et
^I$
$
$ echo "$IFS" | cat -et
^I$
$
$ string="foo bar foo:bar"
$ for i in $string; do echo "[$i] extracted"; done
[foo] extracted
[bar] extracted
[foo:bar] extracted
$ IFS=":" && echo "$IFS" | cat -et
:$
$ for i in $string; do echo "[$i] extracted"; done
[foo bar foo] extracted
[bar] extracted
$ unset IFS && echo "$IFS" | cat -et
$
$ for i in $string; do echo "[$i] extracted"; done
[foo] extracted
[bar] extracted
[foo:bar] extracted
input.csv:
Record is : SNo,Quantity,Price,Value
Record is : 1,2,20,40
Record is : 2,5,10,50
#! /bin/bash
while IFS="," read -r rec1 rec2
do
echo "Displaying Record-$rec1"
echo "Price: $rec2"
done < <(cut -d "," -f1,3 input.csv | tail -n +2)
https://tldp.org/LDP/abs/html/comparison-ops.html
-n
string is not null.
-z
string is null, that is, has zero length
|| :
https://superuser.com/questions/1022374/what-does-mean-in-the-context-of-a-shell-script
命令1 && 命令2→ 只有当命令1执行成功(退出状态码为0)时,才会执行命令2;如果命令1执行失败(退出状态码非0),则命令2不会执行。
# 每天8点执行:如果是假期,则运行test.sh
0 8 * * * /home/user/scripts/condition.sh && /home/user/scripts/test.sh
# condition.sh 片段
Holiday="202601011,20260401"
if [[ $Holiday = *$today* ]]; then
echo "今天是假期"
exit 0 # 条件成立,成功退出
else
echo "今天不是假期"
exit 1 # 条件不成立,失败退出
fi
例子1:Shell运行程序的底层逻辑(fork + exec组合)
当你在终端输入 ./myapp时,Shell(比如bash)实际执行的代码逻辑如下(伪代码+C混合示意):
// Shell的main函数中,处理用户输入"./myapp"的部分
void run_command(char* program_path) {
pid_t pid; // 用于存储fork返回的进程ID
// 1. fork:创建子进程(分身术)
pid = fork(); // 关键:fork会返回两次!
if (pid == -1) {
// fork失败(如内存不足)
perror("fork failed");
return;
} else if (pid == 0) {
// 2. 子进程逻辑:exec替换自身为新程序(夺舍术)
// 参数说明:程序路径、命令行参数、环境变量
execve(program_path, argv, envp);
// ❗️如果execve成功,子进程后续代码不会执行!
// 如果失败(如文件不存在),才会走到下面
perror("execve failed");
exit(1);
} else {
// 3. 父进程(Shell)逻辑:等待子进程结束
int status;
waitpid(pid, &status, 0); // 阻塞等待子进程退出
printf("子进程(PID=%d)已结束,退出状态:%d\n", pid, status);
}
}
pid = fork() 父进程(Shell)分裂出子进程(几乎复制父进程的所有状态:内存、文件描述符、环境变量等)。 关键:fork返回两次:
例子2:验证exec后PID不变的实验代码 这个C程序直接演示:exec不会创建新进程,PID保持不变(因为“夺舍”而非“新生”)。
#include <stdio.h> // printf, perror
#include <unistd.h> // getpid, execlp
#include <stdlib.h> // exit
int main() {
// 1. 打印exec前的PID(当前进程ID)
printf("Before exec: PID = %d (PPID = %d)\n", getpid(), getppid());
// 2. 执行exec:替换为系统命令"ls -l"(列出当前目录文件)
// 参数说明:程序名("ls")、命令行参数("ls", "-l", NULL)、环境变量默认继承
int ret = execlp("ls", "ls", "-l", NULL);
// 3. ❗️如果exec成功,以下代码永远不执行!
if (ret == -1) {
perror("execlp failed"); // 仅当exec失败时执行(如"ls"命令不存在)
exit(1);
}
// 4. 理论上不会走到这里,但写上证明"exec后不返回"
printf("After exec: PID = %d (This line will NOT print!)\n", getpid());
return 0;
}
编译与运行结果
编译:gcc exec_demo.c -o exec_demo
运行:./exec_demo
预期输出(假设当前目录有file1.txt和dir1):
Before exec: PID = 12345 (PPID = 6789) # 12345是当前进程PID,6789是父进程(Shell)PID
total 8
-rw-r--r-- 1 user user 0 Apr 6 22:00 file1.txt
drwxr-xr-x 2 user user 40 Apr 6 22:00 dir1
关键观察:
第一行打印了Before exec的PID(如12345)。
之后execlp(“ls”, …)执行,当前进程被替换为ls程序,输出了ls -l的结果。
After exec那行永远不打印!因为exec成功后,原程序的代码被完全覆盖,不会返回。
| 虽然输出的是ls的结果,但整个过程的PID始终是12345(可通过ps aux | grep 12345验证,进程名会从exec_demo变为ls,但PID不变)。 |
补充:简单版fork返回值示例(验证“返回两次”)
如果想直观感受fork的“分裂魔法”,可运行这个更简单的代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork(); // 分裂进程
if (pid < 0) {
perror("fork failed");
return 1;
} else if (pid == 0) {
// 子进程:fork返回0
printf("👶 子进程:我的PID=%d,父进程PID=%d\n", getpid(), getppid());
} else {
// 父进程:fork返回子进程PID(>0)
printf("👨 父进程:我的PID=%d,子进程PID=%d\n", getpid(), pid);
wait(NULL); // 等待子进程结束(避免僵尸进程)
}
return 0;
}
运行结果(示例):
👨 父进程:我的PID=12345,子进程PID=12346 👶 子进程:我的PID=12346,父进程PID=12345
结论:fork后,父子进程同时执行后续代码,但通过pid值区分身份(父得子PID,子得0)。
#list folders only
ls -d */
L_FOLER_LIST=( $(ls -d */ 2>/dev/null) )
echo "$folder list count: ${#L_FOLER_LIST[@]}"