为什么此脚本在终端中起作用,但不能在文件中起作用?


18

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

#!/bin/sh
html_file=$1
echo "html_file = $html_file"
substr=.pdf
pdf_file="${html_file/.html/.pdf}"
echo "pdf_file = $pdf_file"

如果我将其粘贴到命令行中,则可以正常工作:

$ 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"

html_file = /home/max/for_pauld/test_no_base64.html
pdf_file = /home/max/for_pauld/test_no_base64.pdf

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

但是,当我调用脚本时,

$ saucer "/home/max/for_pauld/test_no_base64.html"

我得到以下输出:

html_file = /home/max/for_pauld/test_no_base64.html
/home/max/bin/saucer: 5: /home/max/bin/saucer: Bad substitution

我的脚本使用的是bash的其他版本还是其他版本?我需要更改我的shebang行吗?


6
该参数扩展是一种bashism(好吧,非POSIX):更改您的shebang。
jasonwryan

谢谢!那行得通。我觉得sh和之间的区别有点模糊bash。我会继续阅读。如果您愿意将您的评论写成答案,那么我将其标记为正确。
Max Williams

向The Duke打勾,请他提供详细的答案,但无论如何还是要感谢。
Max Williams

2
@MaxWilliams:简而言之,sh是原始的bourne外壳。所有兼容的脚本都应为sh编写。但是许多后续的shell(例如bash,Bourne Again shell)是“几乎兼容的”,并增加了很多额外的好处。例如您使用的替代。如今,仅使用posix功能是相当安全的,但是请注意,可移植性取决于更窄的范围(即仅sh)。因此,一般来说,请使用:#!/usr/bin/env bash作为您的Shebang,并根据需要使用posixly定义的替代方法,但要注意可移植性。并阅读:unix.stackexchange.com/a/48787/27616
Olivier Dulac

Answers:


36

什么是嘘

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

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

什么是bash

bash开始时是sh兼容的实现(尽管它比POSIX标准早了几年),但是随着时间的流逝,它获得了许多扩展。这些扩展中的许多扩展名可能会更改有效POSIX Shell脚本的行为,因此,它本身bash不是有效的POSIX Shell。相反,它是POSIX Shell语言的方言。

bash支持一个--posix开关,这使其更符合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使用pdkshKorn shell的后代。FreeBSD sh是原始UNIX Bourne shell的后代。Solaris具有自己的sh名称,长期以来不兼容POSIX。Heirloom项目提供了免费的实现。

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

复杂之处在于它/bin/sh可能是符号链接或硬链接。如果它是符号链接,则一种可解决的便携式方法是:

% file -h /bin/sh
/bin/sh: symbolic link to bash

如果是硬链接,请尝试

% find -L /bin -samefile /bin/sh
/bin/sh
/bin/bash

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

社bang线

最终,由您决定要使用哪一个,即编写“ shebang”行。

例如

#!/bin/sh

将使用sh(以及碰到的任何东西),

#!/bin/bash

/bin/bash在可用的情况下使用(如果不可用,则会失败并显示错误消息)。当然,您也可以指定其他实现,例如

#!/bin/dash

使用哪一个

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

  • 它是标准化的
  • 它更容易学习
  • 它可以跨POSIX系统移植-即使它们恰好没有bash,也必须具有sh

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


感谢您提供详细的答案:我使用的Linux Mint是基于Debian的,/bin/sh的确是的符号链接/bin/dash
Max Williams

如果要坚持使用POSIX sh,则最好完全省略shebang:如果shell命令文件的第一行以字符“#!”开头,则结果未指定 pubs.opengroup.org/onlinepubs/009695399/公用事业/…
Daniel Jour

4
@DanielJour如果任何类似Unix的系统放弃了对shebangs的常规支持,它将破坏很多东西,实际上将无法使用。因此,这是事实上的标准,即使未在POSIX中指定。唯一实际的兼容性问题是解释器的路径可能不同。
巴马尔

23

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

pdf_file="${html_file/.html/.pdf}"

${variable/search/replace}是GNU扩展。但是您可以使用纯POSIX轻松避免这种情况:

pdf_file="${html_file%.html}".pdf

继Hunter之后,这比将shebang更改为 #! /bin/bash


谢谢-我同意这是“更纯粹”的解决方法,但是我宁愿由shebang加载bash,因为bash是我在终端中常规使用的默认设置(默认情况下),使脚本与常规脚本执行相同的操作更容易命令行。
Max Williams

1
@MaxWilliams当然没问题。这主要是针对“问答”数据库的。
Philippos
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.