为什么这个脚本在终端中工作而不是从文件中工作?

Max*_*ams 19 bash string

我将这个 shell 脚本保存在一个文件中:它执行一些基本的字符串替换。

#!/bin/sh
html_file=$1
echo "html_file = $html_file"
substr=.pdf
pdf_file="${html_file/.html/.pdf}"
echo "pdf_file = $pdf_file"
Run Code Online (Sandbox Code Playgroud)

如果我将它粘贴到命令行中,它工作正常:

$ html_file="/home/max/for_pauld/test_no_base64.html"
  echo "html_file = $html_file"
  substr=.pdf
  pdf_file="${html_file/.html/.pdf}"
  echo "pdf_file = $pdf_file"
Run Code Online (Sandbox Code Playgroud)

html_file = /home/max/for_pauld/test_no_base64.html
pdf_file = /home/max/for_pauld/test_no_base64.pdf
Run Code Online (Sandbox Code Playgroud)

这是上面 echo 的输出 - 它按预期工作。

但是,当我调用脚本时,

$ saucer "/home/max/for_pauld/test_no_base64.html"
Run Code Online (Sandbox Code Playgroud)

我得到这个输出:

html_file = /home/max/for_pauld/test_no_base64.html
/home/max/bin/saucer: 5: /home/max/bin/saucer: Bad substitution
Run Code Online (Sandbox Code Playgroud)

我的脚本使用的是不同版本的 bash 还是什么?我需要改变我的shebang线吗?

Hun*_*son 39

什么是sh

sh(或 Shell 命令语言)是一种由POSIX 标准描述的编程语言。它有许多实现(ksh88, dash, ...)。bash也可以被认为是sh(见下文)的实现。

因为sh是规范,而不是实现,/bin/sh是大多数 POSIX 系统上实际实现的符号链接(或硬链接)。

什么是bash

bash一开始是一个sh兼容的实现(尽管它比 POSIX 标准早了几年),但随着时间的推移,它已经获得了许多扩展。许多这些扩展可能会改变有效 POSIX shell 脚本的行为,因此它本身bash不是有效的 POSIX shell。相反,它是 POSIX shell 语言的一种方言。

bash支持--posix开关,使其更符合 POSIX。如果作为sh.

sh = 重击?

长期以来,/bin/sh用于指向/bin/bash大多数 GNU/Linux 系统。结果,忽略两者之间的差异几乎变得安全。但最近这种情况开始发生变化。

/bin/sh不指向/bin/bash(其中一些/bin/bash甚至可能不存在)的系统的一些流行示例是:

  1. 现代Debian和Ubuntu系统,其符号链接shdash默认;
  2. Busybox,它通常在 Linux 系统启动时作为initramfs. 它使用ashshell实现。
  3. BSD,以及通常的任何非 Linux 系统。OpenBSD 使用pdksh,Korn shell 的后代。FreeBSDsh是原始 UNIX Bourne shell 的后代。Solaris 有它自己的sh,它在很长一段时间内都不是 POSIX 兼容的;传家宝项目提供了一个免费的实现。

如何找出/bin/sh系统上的指向?

复杂的是这/bin/sh可能是符号链接或硬链接。如果它是符号链接,解决它的可移植方法是:

% file -h /bin/sh
/bin/sh: symbolic link to bash
Run Code Online (Sandbox Code Playgroud)

如果是硬链接,请尝试

% find -L /bin -samefile /bin/sh
/bin/sh
/bin/bash
Run Code Online (Sandbox Code Playgroud)

事实上,该-L标志涵盖了符号链接和硬链接,但这种方法的缺点是不可移植——POSIX不需要 find支持该-samefile选项,尽管GNU findFreeBSD find 都支持它。

社帮线

最终,由您决定使用哪个,通过编写 «shebang» 行。

例如

#!/bin/sh
Run Code Online (Sandbox Code Playgroud)

将使用sh(以及任何碰巧指向的东西),

#!/bin/bash
Run Code Online (Sandbox Code Playgroud)

/bin/bash在可用时使用(如果不可用则失败并显示错误消息)。当然,你也可以指定另一种实现,例如

#!/bin/dash
Run Code Online (Sandbox Code Playgroud)

使用哪一个

对于我自己的脚本,我更喜欢sh以下原因:

  • 它是标准化的
  • 它更简单,更容易学习
  • 它在 POSIX 系统之间是可移植的——即使他们碰巧没有bash,他们也必须有sh

使用也有好处bash。它的特性使编程更加方便,并且类似于其他现代编程语言中的编程。这些包括范围局部变量和数组之类的东西。Plainsh是一种非常简约的编程语言。

  • @DanielJour 如果任何类 Unix 系统放弃了对 shebangs 的通常支持,它会破坏很多几乎无法使用的东西。所以它是一个事实上的标准,即使它没有在 POSIX 中指定。唯一实际的兼容性问题是解释器的路径可能不同。 (4认同)

Phi*_*pos 23

除了@Hunter.S.Thompson 的出色回答之外,我想指出脚本的不可移植部分是

pdf_file="${html_file/.html/.pdf}"
Run Code Online (Sandbox Code Playgroud)

${variable/search/replace}是一个 GNU 扩展。但是您可以使用纯 POSIX 轻松避免它:

pdf_file="${html_file%.html}".pdf
Run Code Online (Sandbox Code Playgroud)

在 Hunter 之后,这是比将 shebang 更改为更好的解决方案 #! /bin/bash