我正在尝试编写一个函数来替换exit内置函数的功能,以防止自己退出终端。
我曾尝试使用SHLVL环境变量,但它在子外壳中似乎没有改变:
$ echo $SHLVL
1
$ ( echo $SHLVL )
1
$ bash -c 'echo $SHLVL'
2
Run Code Online (Sandbox Code Playgroud)
我的功能如下:
exit () {
if [[ $SHLVL -eq 1 ]]; then
printf '%s\n' "Nice try!" >&2
else
command exit
fi
}
Run Code Online (Sandbox Code Playgroud)
不过,这不允许我exit在子外壳中使用:
$ exit
Nice try!
$ (exit)
Nice try!
Run Code Online (Sandbox Code Playgroud)
检测我是否在子shell中的好方法是什么?
是否可以制作一个类似的功能
function doStuffAt {
cd $1
# do stuff
}
Run Code Online (Sandbox Code Playgroud)
但是让它如此调用该函数实际上并没有改变我的密码,它只是在函数的持续时间内改变它?我知道我可以保存密码并在最后设置它,但我希望有一种方法可以让它在本地发生而不必担心。
Learning Bash Book 提到一个子shell只会继承环境变量和文件描述符等,不会继承没有导出的变量:
$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var
$
Run Code Online (Sandbox Code Playgroud)
据我所知,shell 将为()和 for创建两个子shell ./file,但是为什么在这种()情况下,子shell 会识别var变量,尽管它没有导出,而在这种./file情况下它没有识别?
# Strace for ()
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631
Run Code Online (Sandbox Code Playgroud)
我试图strace弄清楚这是如何发生的,令人惊讶的是我发现 bash 将对克隆系统调用使用相同的参数,所以这意味着分叉进程()和./file应该具有与父进程相同的进程地址空间,那么为什么在这种()情况下,变量是否对子外壳可见,并且这种情况不会发生./file,尽管相同的参数基于克隆系统调用?
根据this和this,子shell是通过使用括号启动的(…)。
( echo "Hello" )
Run Code Online (Sandbox Code Playgroud)
echo "Hello" &
Run Code Online (Sandbox Code Playgroud)
Posix 规范在本页中使用了这个词subshell,但没有定义它,而且,在同一页上,也没有定义“子进程”。
两者都使用核fork()函数,对吗?
那么将一些叉称为“子外壳”而将其他一些叉称为“子进程”的确切区别是什么。
如何获取子shell的pid?
例如:
$ echo $$
16808
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为原始外壳扩展$$:
$ ( echo $$ )
16808
Run Code Online (Sandbox Code Playgroud)
为什么单引号不起作用?原shell去掉单引号后,子shell$$自己不展开了吗?
$ ( echo '$$' )
$$
Run Code Online (Sandbox Code Playgroud)
为什么也eval不起作用?是eval由子shell 运行的吗?为什么它会给我原始外壳的 PID?
$ ( eval echo '$$' )
16808
Run Code Online (Sandbox Code Playgroud)
谢谢。
有这两个名称:subshell和child-shell。
是的,子进程将通过以下任一方式启动:
sh -c 'echo "Hello"'
( echo "hello" )
echo "$(echo "hello")
echo "hello" | cat
Run Code Online (Sandbox Code Playgroud)
是否全部相同并共享相同的名称?所有共享相同的属性吗?
POSIX有这个定义:
一个shell执行环境包括....
但是上面链接的最后一段是这样的:
子shell 环境应创建为shell 环境的副本,除了未被忽略的信号陷阱应设置为默认操作。
特别是:
命令替换、用括号分组的命令和异步列表应在子 shell 环境中执行。另外,多命令管道的每个命令都在一个子shell环境中;....
在sh -c 'echo "Hello"'不包括有,应该是可以称为子shell?
我正在阅读一本 Linux shell 脚本书,并发现以下警告:
“命令替换会创建所谓的子外壳来运行封闭的命令。子外壳是从运行脚本的外壳生成的单独子外壳。因此,您在脚本中创建的任何变量都不能用于子外壳命令”。
我尝试在当前 bash shell 的 CLI 中创建一个变量,然后进入子 shell 检查是否可以在屏幕上打印它。所以是的,我做不到,似乎是根据上面的引文。但是,我已经使用命令替换运行了以下脚本:
#!/bin/bash
var=5.5555
ans=$(echo $var)
echo $ans
Run Code Online (Sandbox Code Playgroud)
结果是:
5.5555
Run Code Online (Sandbox Code Playgroud)
据我了解,它不应该打印 var 的值,因为子外壳不应该能够“看到它”。为什么会发生?
到目前为止,我知道有两种主要方式:
显式:将括号括在命令列表中
隐式:管道中的每个命令
是否有更多的方式,无论是显式还是隐式,在 bash 中创建子shell?
运行命令后
{ sleep 5; } &
Run Code Online (Sandbox Code Playgroud)
输出ps是(输出1)
PID TTY TIME CMD
972 ttys000 0:00.27 -bash
2556 ttys000 0:00.00 -bash
2557 ttys000 0:00.00 sleep 5
Run Code Online (Sandbox Code Playgroud)
而对于
( sleep 5 ) &
Run Code Online (Sandbox Code Playgroud)
出ps是(输出 2)
PID TTY TIME CMD
972 ttys000 0:00.28 -bash
2566 ttys000 0:00.00 sleep 5
Run Code Online (Sandbox Code Playgroud)
()导致子shell环境,我希望在这种情况下为“输出1”,因为它会导致子进程分叉,而我希望{ sleep 5; } &在当前shell中执行时为“输出2” 。这可能看起来是一个愚蠢的问题,但我真的不明白这种行为。
我在这里缺少什么?
$ echo $(echo x; echo y)
x y
$ a='echo x; echo y'
$ echo $($a) # expect 'x y'
x; echo y
Run Code Online (Sandbox Code Playgroud)
eval和的情况下对存储在变量中的命令列表执行命令替换bash -c?echo $(eval $a)并且echo $(bash -c "$a")实际上做了我想要的,但我听说使用 eval 经常是解决问题的错误方法,所以我想知道如何在没有这些命令的情况下进行管理(使用bash -c实际上是一样的)
command-line bash command-substitution variable-substitution
在 ps 的手册页中,它将进程标志 1 列为“进程分叉但未执行”。进程处于这种状态的常见用例/情况是什么?
bash ×10
subshell ×7
shell ×6
process ×4
command ×1
command-line ×1
exit ×1
fork ×1
posix ×1
shell-script ×1