Alb*_*o B 17 bash command-line sh windows-subsystem-for-linux
我已经安装了 wsl,如果从 cmd 提示符运行:
wsl ls
Run Code Online (Sandbox Code Playgroud)
它工作得很好,但如果我创建一个文件 script.sh 并尝试:
wsl script.sh
Run Code Online (Sandbox Code Playgroud)
与里面:
ls
Run Code Online (Sandbox Code Playgroud)
或任何其他 linux 命令,我得到:
/bin/bash: script.sh: command not found
Run Code Online (Sandbox Code Playgroud)
当然,我的脚本位于正确的文件夹中。是什么原因导致这个问题?
编辑:非常感谢您的回答。我是否可以将 .sh 文件关联到 wsl,以便它们通过双击自动运行?
Not*_*1ds 20
从表面上看,这是一个非常简单的问题。我已经回答了一些有关通过wsl命令执行命令的问题。然而,这个问题有点复杂。您通过以下方式为问题添加了两个维度:
这些都不是必需的,但如果您不真正了解幕后发生的事情,它们确实会引起问题。
首先,对你的问题进行“简短回答”,然后我将深入探讨其他技术的解释和缺点。
您有一个简单的脚本script.sh,其中包含以下内容:
ls
Run Code Online (Sandbox Code Playgroud)
您的脚本位于当前目录中。wsl要通过命令(从 PowerShell、CMD 或其他 Windows 进程)运行它,请使用:
wsl -e sh script.sh
Run Code Online (Sandbox Code Playgroud)
但不要这样做。养成使用 Shebang 线的习惯。将脚本编辑为:
#!/usr/bin/env sh
ls
Run Code Online (Sandbox Code Playgroud)
将其设置为对您的默认用户可执行(看来您已经拥有)。然后通过以下方式启动它:
wsl -e ./script.sh
Run Code Online (Sandbox Code Playgroud)
据我所知,这是唯一可以有效运行脚本的两种技术。从命令启动它的所有其他方法wsl都会导致加载多个shell——要么是 shell 内的 shell,要么是 shell exec'ing shell。
-e?它有什么作用?wsl -e(或长格式wsl --exec),根据帮助:
不使用默认的 Linux shell 执行指定的命令
这允许我们完全跳过 shell 或指定不同的 shell,而不会产生运行默认 shell 的开销。
所以当你运行时wsl -e sh script.sh,:
shsh阅读并解释每一行script.sh(请参阅下文以更好地理解“阅读和解释”)当您运行wsl -e ./script.sh带有 shebang 行的脚本时,:
./script.sh/init进程(实际上在所有这些情况下运行,但这是我们真正需要明确提及的唯一一次)看到脚本的第一行是 shebang,然后直接在该 shell 中加载并运行脚本。解释为什么不应该使用其他技术才是真正开始变得复杂的地方。你必须明白:
bash因为这就是您正在使用的外壳。-e命令的参数——wsl使用它与不使用它会发生什么?当然,我们已经在上面介绍过这一点。哇!正确的?!所有的?
因此,考虑到这一点,让我举一些执行脚本的其他方法的示例,为什么它们不起作用,或者为什么它们工作效率较低:
wsl script.sh(你原来的例子)您可能认为这与从 Linux/WSL 内部运行相同bash script.sh,但事实并非如此。
bash script.sh(在 WSL 内部)有效,因为它正在读取脚本中的每一行并将其解释bash为要由其自身执行的命令。由于它不执行脚本,因此不需要路径。shell 只读取当前目录中的文件。
wsl script.sh(WSL 之外)不起作用,因为它实际上尝试使用默认用户的 shell(在本例中)执行脚本bash。这大致相当于bash -c script.sh从 WSL/Linux 内部进行调用。要执行文件,任一文件需要位于搜索路径(由PATH环境变量表示)中的目录中,或者您需要提供文件的绝对或相对路径(例如bash -c ./script.sh或wsl ./script.sh)。
有关执行脚本和解释脚本之间差异的更多阅读(尽管它没有使用确切的术语)可以在这个优秀的 Unix & Linux 堆栈答案中找到。
wsl sh script.sh(或者wsl sh ./script.sh)其中任何一个都会运行您的脚本,但它们实际上会加载 shell 两次。这:
bash)bash执行( -c)sh script.shbash转身 and exec's sh(用进程替换bash进程shsh 读取并解释(而不是执行)您的脚本。因为sh正在读取和解释您的脚本,所以它可以从当前目录执行此操作,而无需路径(或文件位于 上的目录中)$PATH。
但加载两个不同的 shell 是没有意义的。原始版本wsl -e sh script.sh仅运行sh并bash完全跳过,从而节省了加载时间。
注意:在这种情况下,是否有 shebang 行并不重要,因为 shebang 仅在执行脚本时发挥作用。 在阅读和解释sh时将该行视为注释并跳过它。
wsl ./script.sh(没有 Shebang 线)还装载两个炮弹。请记住,这就像跑步bash -c ./script.sh。它:
bash(或您的默认 shell,如果不同的话)./script.sh从当前目录执行exec是一个新bash进程,替换当前进程,并在其中执行脚本。wsl ./script.sh(用舍邦线)它与“no shebang”情况非常相似,但 Bash 不会退回到“后备”,而是使用您在 shebang 行上告诉它的任何内容(sh在本例中)。
它仍然exec是一个实例sh,替换父进程,调用bash进程。
wsl -e sh -c ./script.sh这一定行得通,对吧?我的意思是,我们告诉 WSL 加载sh并执行脚本——它还能做什么?
是的,再次,它有效。但我们再次加载 shell 两次。一次显式(通过-e),一次 shell 确定如何执行脚本(通过 shebang 或后备)。
通过将脚本更改为:
ps -ef
Run Code Online (Sandbox Code Playgroud)
没有 shebang,wsl -e sh -c ./script.sh返回:
PID TTY TIME CMD
2638 pts/1 00:00:00 sh
2639 pts/1 00:00:00 sh
2640 pts/1 00:00:00 ps
Run Code Online (Sandbox Code Playgroud)
使用 shebang,wsl -e sh -c ./script.sh返回:
PID TTY TIME CMD
2643 pts/1 00:00:00 sh
2644 pts/1 00:00:00 test.sh
2645 pts/1 00:00:00 ps
Run Code Online (Sandbox Code Playgroud)
根据我们的提议wsl -e ./script.sh,我们看到:
PID TTY TIME CMD
2651 pts/1 00:00:00 test.sh
2652 pts/1 00:00:00 ps
Run Code Online (Sandbox Code Playgroud)
如果这还不足以给您带来 shell/script/shebang 噩梦,那么请注意,有时您会想要执行父 shell,即使这意味着转身并加载子进程。
如果您的脚本需要启动文件中的某些内容,则可能会出现这种情况。执行上述任何命令行时,WSL 将 shell 作为非登录、非交互式 shell 启动。
这意味着您的~/.bashrc和不会被处理,如果您对它们进行了脚本期望的~/.bash_profile更改(例如 或其他一些环境变量),这可能会导致一些混乱。PATH
在这种情况下,请调用类似以下内容:
wsl -e bash -lic ./script.sh
Run Code Online (Sandbox Code Playgroud)
这将强制“登录,交互式”shell 并处理您的启动配置。
请注意,也可能可以修改您的 shebang 行来强制执行此操作,但我将跳过任何相关说明,因为这个答案/书籍/论文已经变得足够长了;-)
不过,如果您需要更多阅读,我会向您指出这个问题。
启动脚本的一般方法不是简单的script.sh,而是:
sh script.sh
Run Code Online (Sandbox Code Playgroud)
因此,使用wsl,您可以尝试:
wsl sh script.sh
Run Code Online (Sandbox Code Playgroud)
这应该够了吧。