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()函数,对吗?
那么将一些叉称为“子外壳”而将其他一些叉称为“子进程”的确切区别是什么。
我期望cat <(cat)并cat | cat做同样的事情:将行从标准输入复制到标准输出。我的理解是,两者都将cat在 subshell 中执行 a ,将 subshellcat的 stdout重定向到临时命名管道,然后cat在当前 shell 中执行另一个并将其 stdin 重定向到管道。
相反,cat <(cat)让我在终端上输入,但没有任何输入行被复制并且^D无法发出信号EOF;cat | cat虽然按预期工作。
作为进一步的实验,我检查了是否cat =(cat)有与 类似的困难cat <(cat),但它按我的预期工作:直到 a 的所有标准输入^D都被一次性复制到标准输出。
任何人都可以帮助我了解 zsh 在幕后做了什么?
我想在 Bash 中实现一个函数,它在每次调用时增加(并返回)一个计数。不幸的是,这似乎很重要,因为我在子 shell 中调用该函数,因此它无法修改其父 shell 的变量。
这是我的尝试:
PS_COUNT=0
ps_count_inc() {
let PS_COUNT=PS_COUNT+1
echo $PS_COUNT
}
ps_count_reset() {
let PS_COUNT=0
}
Run Code Online (Sandbox Code Playgroud)
这将按如下方式使用(因此我需要从子shell调用函数):
PS1='$(ps_count_reset)> '
PS2='$(ps_count_inc) '
Run Code Online (Sandbox Code Playgroud)
这样,我就会有一个带编号的多行提示:
> echo 'this
1 is
2 a
3 test'
Run Code Online (Sandbox Code Playgroud)
可爱的。但由于上述限制不起作用。
一个无效的解决方案是将计数写入文件而不是变量。但是,这会在多个同时运行的会话之间产生冲突。当然,我可以将 shell 的进程 ID 附加到文件名。但我希望有一个更好的解决方案,不会让我的系统因大量文件而混乱。
我最近在使用 bash 时遇到了一些奇怪的问题。在尝试简化我的脚本时,我想出了一小段代码:
$ o(){ echo | while read -r; do return 0; done; echo $?;}; o
0
$ o(){ echo | while read -r; do return 1; done; echo $?;}; o
1
Run Code Online (Sandbox Code Playgroud)
return应该退出函数而不打印$?,不是吗?好吧,然后我检查了我是否可以单独从管道返回:
$ echo | while read -r; do return 1; done
bash: return: can only `return' from a function or sourced script
Run Code Online (Sandbox Code Playgroud)
没有while循环也会发生同样的情况:
$ foo(){ : | return 1; echo "This should not be printed.";}
$ foo
This should not be printed. …Run Code Online (Sandbox Code Playgroud) 我尝试了以下脚本:
#!/bin/bash
trap 'echo "touching a file" && touch $FILE' EXIT
foo1(){
echo "foo1"
}
foo(){
echo "foo"
export FILE=${FILE:-/tmp/file1}
}
(foo1)
foo
Run Code Online (Sandbox Code Playgroud)
上述脚本的输出是:
[root@usr1 my_tests]# ./test.sh
foo1
foo
touching a file
Run Code Online (Sandbox Code Playgroud)
但是,我希望在退出时foo1也调用 trap ,这在子shell中调用。
trap由子shell继承?trap由子shell继承?在我搜索列出所有 shell 变量的命令时,我以某种方式意识到,有一个命令可以列出所有环境变量,但不知何故,由于我不知道的原因,没有人列出所有 shell 变量。
但是,这里有人给出了如何显示所有变量、shell 和环境变量的答案。
( set -o posix ; set ) | less
Run Code Online (Sandbox Code Playgroud)
他其实并没有为外行解释这个表达是做什么的,我的零碎理解还不足以掌握它背后的想法。
这是我所知道的:
set是某种声明变量的方法,虽然不知道-o posix是什么意思以及为什么要连续执行第二个set
命令 | 少这个不是问题,即使我理解它,它是一个寻呼机,可以更好地控制输出。
我使用带有本机 Bash 的 Ubuntu 16.04。
我不确定是否执行
#!/bin/bash
myFunc() {
export myVar="myVal"
}
myFunc
Run Code Online (Sandbox Code Playgroud)
在任何意义上都等于,只是执行export myVar="myVal".
当然,全局变量通常应该在函数之外声明(我认为这是约定俗成的问题,即使技术上可行)但我确实想知道更奇特的情况,即编写一些非常通用的函数并希望在其中添加一个变量仍然可以在任何地方使用。
将export一个函数中的变量,等同于全球出口了,直接在命令行,使其可用于一切都在外壳(所有子shell,以及功能在其内部)?
如何获取子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)
谢谢。
我知道我可以使用这样的环境变量运行命令:
FOO=bar mycommand
Run Code Online (Sandbox Code Playgroud)
我知道我可以在这样的子 shell 中运行命令:
(firstcommand && secondcommand)
Run Code Online (Sandbox Code Playgroud)
但是我可以以某种方式将这两者结合起来吗?
FOO=bar (firstcommand && secondcommand)
Run Code Online (Sandbox Code Playgroud)
给出:
Run Code Online (Sandbox Code Playgroud)sh: syntax error: unexpected "("
至少在busybox shell(灰烬)中。
编辑: Kusalananda 建议FOO=bar sh -c 'first && second'这确实是一个解决方案。但是,我也对替代答案感兴趣,因为我喜欢 subshell 语法,因为它不需要摆弄转义引号。