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

hl0ma9xz  于 2023-04-12  发布在  Unix
关注(0)|答案(5)|浏览(256)

通过阅读这个线程,看起来可以使用shebang来运行Rust *。

#!/usr/bin/env rustc

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

使其可执行并运行会编译,但不会运行代码。

chmod +x hello_world.rs
./hello_world.rs

但是,这只会将代码编译为hello_world
*.rs文件可以像shell脚本一样直接执行吗?

  • 这引用了rustx,我研究了一下,但它是一个bash脚本,每次都编译脚本(没有缓存),从不从temp目录中删除文件,尽管这可以改进。
abithluo

abithluo1#

还有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!

要使用来自www.example.com的板条crates.io,请参阅上面链接的README中的教程。

57hvy0tb

57hvy0tb2#

这似乎起作用:

#!/bin/sh
//usr/bin/env rustc $0 -o a.out && ./a.out && rm ./a.out ; exit

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

woobm2wo3#

我写了一个工具,就是为了这个:Scriptisto。它是一个完全与语言无关的工具,它可以与其他编译语言或具有昂贵验证步骤的语言(Python和mypy)一起使用。
对于Rust,它还可以在幕后获取dependencies,或者完全在Docker中构建,而无需安装Rust编译器。scriptisto将这些模板嵌入到二进制文件中,以便您可以轻松引导:

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

你可以使用new docker-rust来代替new rust,并且构建不需要在你的主机系统上使用Rust编译器。

iugsix8n

iugsix8n4#

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

需要货物播放。您可以看到一个不需要任何东西的解决方案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 "$0")) | 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 "$0" | 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*' "$0"; then
        # Compile the input script as-is to the previously determined location.
        rustc "$0" -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 $0)" | 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: */
oymdgrw7

oymdgrw75#

虽然其他答案都很好,但我想要一种简单的方法来编译,缓存和运行一个独立的脚本。我的理由是,如果我发布的脚本依赖于安装的rust,我可能不能同时依赖于安装的一些第三方库来编译它。
如果我需要传递多个文件,我可能只传递一个预编译的二进制文件。如果我的脚本/程序的用例足够复杂,那么我可能在git repo中通过标准的cargo build进程。
因此,对于仅依赖于rust和标准库的单个文件,请使用如下所示的hello.rs文件:

#!/bin/sh
//bin/bash -ec '[ "$0" -nt "${0%.*}" ] && rustc "$0" -o "${0%.*}"; "${0%.*}" "$@"' "$0" "$@"; exit $?

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() > 1 {
        println!("Hello {}!", &args[1]);
    } else {
        println!("Hello world!");
    }
}

为了帮助理解shebang在做什么,尝试这个。它做同样的事情,但更容易维护:

#!/bin/sh
//bin/bash -exc 'source_file="$0"; exe_file="${source_file%.*}"; [ "$source_file" -nt "$exe_file" ] && rustc "$source_file" -o "$exe_file"; "$exe_file" "$@"' "$0" "$@"; exit $?

此解决方案基于wimh的解决方案,但具有以下附加功能:
1.将编译后的脚本,也就是程序,缓存在脚本所在的目录中。当脚本的目录被添加到路径中时,这也有效。
1.将脚本的命令行参数传递给程序。
1.脚本的退出代码与程序或编译器的退出代码相同。
注意:shebang脚本依赖于具有某种文件后缀的脚本文件,例如.rs.sh。如果没有这些,编译器将抱怨用生成的可执行文件覆盖脚本文件。
我的测试表明,与直接运行编译后的程序相比,该脚本增加了大约10 ms的开销。
编辑:有一个RFC in progress添加一个类似于Scriptisto的解决方案到生 rust 的核心,使一个标准的方式来解决OP的问题。

相关问题