我知道我可以使用这样的环境变量运行命令:
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 语法,因为它不需要摆弄转义引号。
有这两个名称: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?
在 bash 中,我想将当前工作目录分配给一个变量。使用子shell,我可以做到这一点。
var=$(pwd)
echo $var
/home/user.name
Run Code Online (Sandbox Code Playgroud)
如果我像这样使用进程替换:
var=<(pwd)
echo $var
/dev/fd/63
Run Code Online (Sandbox Code Playgroud)
我知道进程替换主要在程序不接受 STDIN 时使用。我不清楚进程替换究竟做了什么以及为什么它分配/dev/fd/63给var.
考虑到大量与 Bash 中的子 shell 相关的问题(“为什么我的变量在这个管道while read循环中没有增加?”),我只是认为参考一些编辑器或服务代码会非常好将在子 shell 中执行的格式与在父 shell 中执行的代码格式不同。它可能作为一种教育设备很有用(查看code | code和code < <(code)立即之间的区别)。这存在吗?
如果有人为 jEdit 或 VIm 实现它,则加分。
显然,这并不一定是完美的(没有语法高亮,在我的经验),但我怀疑的东西喜欢(foo=bar; echo $foo;)和command | while read ...不应该一开始太困难了。
我编写了一个快速而肮脏的脚本来计时来自 Web 服务的一些报告:
BASE_URL='http://example.com/json/webservice/'
FIRST=1
FINAL=10000
for report_code in $(seq 1 $FINAL); do
(time -p response=$(curl --write-out %{http_code} --silent -O ${BASE_URL}/${report_code}) ) 2> ${report_code}.time
echo $response # <------- this is out of scope! How do I fix that?
if [[ $response = '404' ]]; then
echo "Deleting report # ${report_code}!"
rm ${report_code}
else
echo "${report_code} seems to be good!"
fi
done
Run Code Online (Sandbox Code Playgroud)
我需要将time命令包装在一个子 shell 中,以便我可以重定向它的输出,但这使得$response父 shell的值不可用。我该如何解决这个问题?
我经常使用子外壳运行命令,有时希望在运行某些东西之前扩展子外壳。这样我可以验证我在做什么,并可能编辑即将发生的事情。
例如,如何在运行之前扩展以下命令行,以便我可以编辑子shell的结果?
例如
$ find -name "test.txt"
/tmp/test.txt
$ mv $(!!) /tmp/new.txt
Run Code Online (Sandbox Code Playgroud)
我希望在运行命令之前看到扩展的子shell ,如下所示:
$ mv /tmp/test.txt /tmp/new.txt
Run Code Online (Sandbox Code Playgroud)
有没有办法做到这一点?
TL;DR:为什么 POSIX 括号组在{保留字后需要空格,而子外壳在保留字后不需要(?
POSIX shell 语法定义花括号组和子shell如下
brace_group : Lbrace compound_list Rbrace
subshell : '(' compound_list ')'
Run Code Online (Sandbox Code Playgroud)
现在,如果我们从字面上看,空格很重要。这意味着必须有空间描述开始和结束括号和括号,如
{ echo hello world; }
( echo hello world )
Run Code Online (Sandbox Code Playgroud)
这也符合复合命令定义:
这些复合命令中的每一个在开头都有一个保留字或控制运算符,并在末尾有一个相应的终止符保留字或运算符。
然而,没有意义的是为什么(list)并且( list )工作得很好((不需要后面的空格),但是大括号扩展必须有一个前导空格,即{echo hello;}不起作用。
当然,保留字被视为 shell 字是有意义的,之后需要一个空格来与字段拆分的概念保持一致,但是定义本身并没有提到空格。此外,如果{和(都被复合命令的 POSIX 定义视为保留字,为什么在这些保留字之后的空格字符方面对它们的处理方式不同?现在,ksh(1)手册确实说明:
作为字符序列的单词由不带引号的空白字符(空格、制表符和换行符)或元字符(<、>、|、;、&、(和))分隔
换句话说,它是有道理的ksh的将认识到(作为字符,其中第一字将是一个命令或变量赋值。POSIX,但是似乎没有提到(作为元字符。就 POSIX 语法而言,我发现的唯一可能的解释是它{被认为是一个“标记”,而 as(没有被列为一个。
/* These are reserved words, not …Run Code Online (Sandbox Code Playgroud) 我认为set -e对子外壳和顶级外壳有相同的影响。显然,事实并非如此。这个:
(
set -e
false
true
) || echo false1
bash -ec '
set -e
false
true
' || echo false2
bash <<EOF || echo false3
set -e
false
true
EOF
bash <<EOF || echo false4
false
true
EOF
bash <<EOF || echo false5
false &&
true
EOF
Run Code Online (Sandbox Code Playgroud)
印刷
false2
false3
false5
Run Code Online (Sandbox Code Playgroud)
这是在哪里记录的?我可以让子外壳程序在错误时终止,而不用&&(或|| exit $?在每个命令之后不执行)连接所有命令吗?
编辑:
我的特定用例是这样的:
set -e
# ...
status=0
( false; true ) || status=$?
report_code $status
return $status
Run Code Online (Sandbox Code Playgroud)
子shell的内容是我的实际代码。这样做的问题是它总是将状态设置为 …
我对在子 shell 中运行命令的理解是,当前 shell 被分叉,然后子 shell 进一步执行fork所需exec的命令。
在 bash 和 zsh 中运行以下命令时,我看到奇怪的行为:
$ ( sleep 5 && sleep 6 )
在bash中运行时:
$ echo $$
6410
$ ( sleep 5 && sleep 6 )
Run Code Online (Sandbox Code Playgroud)
ps给出以下内容:
CMD:睡眠 5
PID:6590
PPID:6589
然后:
CMD:睡眠 6
PID:6616
PPID:6589
这对我来说都是有道理的。sleep 5并且sleep 6两者具有相同的父级(大概是子shell)。
但是,在zsh中,我得到以下信息:
$ echo $$
1987
$ ( sleep 5 && sleep 6 )
Run Code Online (Sandbox Code Playgroud)
ps给出以下内容:
CMD:睡眠 5
PID: …
脚本通常以 shebang 开头,例如#!/usr/bin/env bash,它指定要用于执行的 shell。shebang 不存在时的执行行为似乎取决于调用 shell。无论哪种方式,脚本都是从“新”shell 运行的,该 shell 不知道调用 shell 中定义的变量和函数。
或者,也可以将脚本source放入 shell 中,根据我的理解,这相当于将脚本内容复制粘贴到当前 shell 中。如果脚本中定义了任何函数或变量,那么它们在“执行”后将保留在调用 shell 中,这可能是不可取的。
这两个选项之间有什么关系吗?是否可以将脚本作为调用 shell 的子 shell 来执行,这样我们就可以访问调用 shell 中定义的所有内容,但不会修改它(除非可能使用export这样的命令)?
写作(source myscript.sh)似乎就是我所追求的;这是正确的做法吗?是否有一个等效的 shebang 可以通过调用来产生相同的行为./myscript.sh?