如何在 Unix 系统上直接执行 Rust 代码? (使用 shebang)

How to execute Rust code directly on Unix systems? (using the shebang)

从阅读 this thread 来看,似乎可以使用 shebang 来 运行 Rust *.

#!/usr/bin/env rustc

fn main() {
    println!("Hello World!");
}

使此可执行文件和 运行ning 编译,但 运行 代码不编译。

chmod +x hello_world.rs
./hello_world.rs

然而,这只会将代码编译成 hello_world

是否可以直接执行*.rs个文件,类似于shell脚本?


* 这引用了 rustx,我对此进行了调查,但它是一个 bash 脚本,它每次都编译脚本(没有缓存)并且从不从临时目录中删除文件,尽管这可以改进。它还具有不能使用板条箱的重大限制。

这似乎有效:

#!/bin/sh
//usr/bin/env rustc [=10=] -o a.out && ./a.out && rm ./a.out ; exit

fn main() {
    println!("Hello World!");
}

cargo-script。这也让您可以使用依赖项。

通过 cargo install cargo-script 安装 cargo-script 后,您可以像这样创建脚本文件 (hello.rs):

#!/usr/bin/env run-cargo-script

fn main() {
    println!("Hello World!");
}

要执行它,您需要:

$ chmod +x hello.rs
$ ./hello.rs
   Compiling hello v0.1.0 (file://~/.cargo/.cargo/script-cache/file-hello-d746fc676c0590b)
    Finished release [optimized] target(s) in 0.80 secs
Hello World!

要使用 crates.io 中的 crate,请参阅上面链接的 README 中的教程。

我专门为此编写了一个工具:Scriptisto。它是一个完全与语言无关的工具,它可以与其他编译语言或具有昂贵验证步骤的语言一起使用(Python with mypy)。

对于 Rust,它还可以获取 dependencies behind the scenes or build entirely in Docker 而无需安装 Rust 编译器。 scriptisto 将这些模板嵌入到二进制文件中,因此您可以 bootstrap 轻松地:

$ scriptisto new rust > ./script.rs
$ chmod +x ./script.rs
$ ./script.rs

您可以 new docker-rust 而不是 new rust 并且构建不需要在您的主机系统上安装 Rust 编译器。

#!/bin/sh
#![allow()] /*
            exec cargo-play --cached --release [=10=] -- "$@"
                        */

需要货运。你可以看到一个不需要任何东西的解决方案here:

#!/bin/sh
#![allow()] /*

# rust self-compiler by Mahmoud Al-Qudsi, Copyright NeoSmart Technologies 2020
# See <https://neosmart.net/blog/self-compiling-rust-code/> for info & updates.
#
# This code is freely released to the public domain. In case a public domain
# license is insufficient for your legal department, this code is also licensed
# under the MIT license.

# Get an output path that is derived from the complete path to this self script.
# - `realpath` makes sure if you have two separate `script.rs` files in two
#   different directories, they get mapped to different binaries.
# - `which` makes that work even if you store this script in $PATH and execute
#   it by its filename alone.
# - `cut` is used to print only the hash and not the filename, which `md5sum`
#   always includes in its output.
OUT=/tmp/$(printf "%s" $(realpath $(which "[=11=]")) | md5sum | cut -d' '  -f1)

# Calculate hash of the current contents of the script, so we can avoid
# recompiling if it hasn't changed.
MD5=$(md5sum "[=11=]" | cut -d' '  -f1)

# Check if we have a previously compiled output for this exact source code.
if !(test -f "${OUT}.md5" && test "${MD5}" = "$(cat ${OUT}.md5)"); then
    # The script has been modified or is otherwise not cached.
    # Check if the script already contains an `fn main()` entry point.
    if grep -Eq '^\s*(\[.*?\])*\s*fn\s*main\b*' "[=11=]"; then
        # Compile the input script as-is to the previously determined location.
        rustc "[=11=]" -o ${OUT}
        # Save rustc's exit code so we can compare against it later.
        RUSTC_STATUS=$?
    else
        # The script does not contain an `fn main()` entry point, so add one.
        # We don't use `printf 'fn main() { %s }' because the shebang must
        # come at the beginning of the line, and we don't use `tail` to skip
        # it because that would result in incorrect line numbers in any errors
        # reported by rustc, instead we just comment out the shebang but leave
        # it on the same line as `fn main() {`.
        printf "fn main() {//%s\n}" "$(cat [=11=])" | rustc - -o ${OUT}
        # Save rustc's exit code so we can compare against it later.
        RUSTC_STATUS=$?
    fi

    # Check if we compiled the script OK, or exit bubbling up the return code.
    if test "${RUSTC_STATUS}" -ne 0; then
        exit ${RUSTC_STATUS}
    fi

    # Save the MD5 of the current version of the script so we can compare
    # against it next time.
    printf "%s" ${MD5} > ${OUT}.md5
fi

# Execute the compiled output. This also ends execution of the shell script,
# as it actually replaces its process with ours; see exec(3) for more on this.
exec ${OUT} $@

# At this point, it's OK to write raw rust code as the shell interpreter
# never gets this far. But we're actually still in the rust comment we opened
# on line 2, so close that: */