AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 449593
Accepted
JoL
JoL
Asked: 2018-06-14 08:01:39 +0800 CST2018-06-14 08:01:39 +0800 CST 2018-06-14 08:01:39 +0800 CST

在脚本运行时解析脚本是普遍存在于 shell 中还是存在于其他解释器中,它是如何工作的?

  • 772

我一直认为 shell 会解析整个脚本,构建 AST,然后从内存中执行该 AST。但是,我刚刚阅读了 Stéphane Chazelas 的评论,并测试了执行此脚本 edit-while-executing.sh:

#!/bin/bash

echo start
sleep 10

然后在它睡觉时运行:

$ echo "echo end" >> edit-while-executing.sh

它的工作是让它在最后打印“结束”。

但是,在尝试修改时:

#!/bin/bash

while true; do
  echo yes
done

通过做:

$ printf "%s" "no " | dd of=edit-while-executing.sh conv=notrunc seek=35 bs=1

它没有用,并一直打印“是”。

我也想知道其他非shell解释器是否也这样工作,并用python尝试了第一个脚本的等价物,但没有用。不过,也许 python 不再是解释器,而更像是一个 JIT 编译器。

所以重申我的问题,这是一种普遍存在于 shell 中并仅限于它们的行为,还是也存在于其他解释器(那些不被视为 shell 的解释器)中?另外,这是如何工作的,我可以做第一次修改而不是第二次修改吗?

shell-script shell
  • 3 3 个回答
  • 351 Views

3 个回答

  • Voted
  1. thrig
    2018-06-14T09:10:18+08:002018-06-14T09:10:18+08:00

    此功能存在于其他提供所谓的 a 的解释器中read eval print loop。LISP 是一种相当古老的语言,具有这样的功能,而 Common LISP 有一个read函数,可以在此处读取表达式(+ 2 2),然后可以将其传递给eval评估(尽管在实际代码中,出于各种安全原因,您可能不想这样做):

    % sbcl
    * (defparameter sexp (read))
    (+ 2 2)
    
    SEXP
    * (print (eval sexp))
    
    4
    4
    

    我们还可以定义我们自己的非常简单的 REPL,而无需太多功能或调试或其他任何东西,但这确实显示了 REPL 部分:

    * (defun yarepl () (loop (print (eval (read))) (force-output) (fresh-line)))
    
    YAREPL
    * (yarepl)
    (* 4 2)
    
    8
    (print "hi")
    
    "hi"
    "hi"
    

    基本上就像铭牌上说的那样,数据被读入、评估、打印,然后(假设没有发生任何崩溃并且仍然有电或为设备供电的东西)它循环回到读取不需要提前建立 AST。(出于显示原因,SBCL 需要force-output和fresh-line添加,其他 Common LISP 实现可能会也可能不会。)

    REPL 的其他内容包括 TCL(“被放射性 LISP 咬住的外壳”),其中包括 Tk 的图形内容

    % wish
    wish> set msg "hello"
    hello
    wish> pack [label .msg -textvariable msg]
    wish> wm geometry . 500x500
    wish> exit
    

    或 FORTH 在这里定义一个函数f>c来进行温度转换(“ ok” 由添加gforth):

    % gforth
    Gforth 0.7.3, Copyright (C) 1995-2008 Free Software Foundation, Inc.
    Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
    Type `bye' to exit
    : f>c ( f -- c ) 32 - 5 9 */ cr . cr ;  ok
    -40 f>c
    -40
     ok
    100 f>c
    37
     ok
    bye
    
    • 3
  2. Best Answer
    ilkkachu
    2018-06-14T09:39:28+08:002018-06-14T09:39:28+08:00

    所以,这会在 Bash/dash/ksh/zsh 中无限期地运行(或者至少直到你的磁盘填满):

    #!/bin/sh
    s=$0
    foo() { echo "hello"; echo "foo" >> $s; sleep .1; }
    foo
    

    需要注意的是,只有在 shell 读取的最后一行之后添加到脚本文件的内容才有意义。如果输入是管道,shell 不会返回重新读取较早的部分,他们甚至不能这样做。

    类似的构造在 Perl 中不起作用,它在运行之前读取整个文件。

    #!/usr/bin/perl -l    
    open $fd, ">>", $0;
    sub foo { print "hello"; print $fd 'foo;' }
    foo;
    

    我们可以看到,当通过管道输入时,它也会这样做。这会在 1 秒后给出一个语法错误(且仅此错误):

    $ (echo 'printf "hello\n";' ; sleep 1 ; echo 'if' ) | perl 
    

    当相同的脚本通过管道传输到 Bash 时,打印hello,然后在一秒钟后抛出语法错误。

    Python 看起来类似于具有管道输入的 Perl,即使解释器在交互时运行读取-评估-打印循环。


    除了逐行读取输入脚本之外,至少 Bash 和 dasheval一次一行地处理参数:

    $ cat evaltest.sh
    var='echo hello
    fi'
    eval "$var"
    $ bash evaltest.sh
    hello
    evaltest.sh: eval: line 4: syntax error near unexpected token `fi'
    evaltest.sh: eval: line 4: `fi'
    

    Zsh 和 ksh 立即给出错误。

    与源脚本类似,这次 Zsh 也像 Bash 和 dash 一样逐行运行:

    $ cat sourceme.sh
    echo hello
    fi
    $ zsh -c '. ./sourceme.sh'
    hello
    ./sourceme.sh:2: parse error near `fi'
    
    • 2
  3. muru
    2018-06-14T08:14:21+08:002018-06-14T08:14:21+08:00

    至少有一个壳,鱼,没有表现出这种行为(但鱼在其他方面是不寻常的):

    % for sh in zsh mksh fish dash bash tcsh; do echo 'sleep 5' > foo.$sh; $sh foo.$sh & sleep 1; echo 'echo $0' >> foo.$sh; fg; done
    [2] 7381
    [2]  - 7381 running    $sh foo.$sh
    foo.zsh
    [2] 7385
    [2]  - 7385 running    $sh foo.$sh
    foo.mksh
    [2] 7387
    [2]  - 7387 running    $sh foo.$sh
    [2] 7390
    [2]  - 7390 running    $sh foo.$sh
    foo.dash
    [2] 7393
    [2]  - 7393 running    $sh foo.$sh
    foo.bash
    [2] 7415
    [2]  - 7415 running    $sh foo.$sh
    foo.tcsh
    

    (此答案的先前版本对 Python 和 Ruby 的观察有误。)

    • 1

相关问题

  • 打印文件行及其长度的脚本[关闭]

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 按分隔符拆分并连接字符串问题

  • 如何将带有〜的路径保存到变量中?

  • MySQL Select with function IN () with bash array

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve