我正在阅读 Dougherty 和 Robbins 合著的《sed & awk》一书。其中一个例子要求将输出通过管道传输到 shell 脚本:
sed -f nameState list | byState
但我发现,为了正常工作,我必须使用chmod授予“byState”脚本执行权限,并使用点斜杠调用它:
sed -f nameState list | ./byState
总是这样吗?这本书是 1997 年出版的,所以也许 shell 标准已经改变了?还是因为我使用的是bash而不是sh?
编辑:这是 byState 脚本:
#! /bin/sh
awk -F, ’{
print $4 ", " $0
}’ $* |
sort |
awk -F, ’
$1 == LastState {
print "\t" $2
}
$1 != LastState {
LastState = $1
print $1
print "\t" $2
}’
执行不包含的命令名
/
适用于要在¹中列出的目录之一中查找的命令$PATH
。您不会想rm file
运行rm
恰好位于当前工作目录中而不是标准rm
²中的命令。如果您想要运行
byState
在特定目录中找到的可执行文件,而不是在任何$PATH
目录中找到的第一个可执行文件(如果有),则需要通过其路径调用它。由于必须至少包含一个/
,对于当前工作目录中的可执行文件,您通常使用./byState
which,就像byState
是该文件的相对路径,但包含一个/
。您也可以使用/full/path/to/byState
或././byState
或../here/byState
which 也可以。如果您希望系统上的任何人都
byState
可以执行该脚本,而不管他们的工作目录如何,您可以要求系统管理员将其副本放入(或默认的任何目录,尽管这是用于本地安装命令的位置)。或者你甚至可以为它制作一个软件包(就像基于 Debian 的系统的文件一样),例如包括该可执行文件及其手册页,其中会列出它的依赖项(,,尽管那些是始终会安装且不需要列为依赖项的必备软件)并要求管理员安装它(就像在基于 Debian 的系统上使用 / / 一样),然后可以将其放入。这样会使其更干净,因为它将被列为已安装的软件,并包含有关其维护者的信息、相应文件的位置,并使其更容易卸载。byState
/usr/local/bin
$PATH
/usr/local/bin
.deb
sh
awk
sort
gdebi
apt
dpkg
/usr/bin
如果
byState
您只适合这样做,您可以将其添加到您自己的目录中,然后将该目录添加到您的$PATH
环境变量中。典型的目录是~/bin
或~/.local/bin
。无论如何,为了能够执行文件,显然您需要对它具有执行权限。对于脚本,您还需要文件具有读取权限,因为解释器(此处
/bin/sh
)需要能够读取其中的代码才能执行它。将授予所有人 (
a
ll)r
ead 和 ex
ecute 权限。或者,您可以使用以下命令完整指定权限:授予所有人读取和执行权限,同时授予所有者写入权限。或者
chmod 755 byState
使用八进制形式。现在您的脚本存在一些问题
sh
(虽然不是bash
脚本,但bash
可以通过将 shebang 更改为来解释它,#! /path/to/bash -
因为它的语言具有兼容的语法):可能是复制粘贴问题,但这些
’
是错误的引号,这些是 U+2019 右单引号。shell 识别为强引号运算符的引号是'
,U+0027 撇号。$*
在列表上下文中不加引号是没有任何意义的³。$*
只有加引号时才有意义,但那是将位置参数与第一个字符$IFS
4连接起来,这不是您在这里想要的。要将所有位置参数逐字传递给awk
,您需要"$@"
(必须加引号)。awk
存在这样的问题,如果你传递一个文件名,比如foo=bar.txt
,它会被视为 awk 变量赋值。所以在这里,你最好这样做5:使用
print $4 ", " $0
,您除了 之外还添加了一个空格,
,因此当您在该输出上执行 时,print "\t" $2
这将打印制表符、空格和字段。您可能希望输入字段分隔符(FS
由 设置的-F
)和输出字段分隔符(OFS
)都只是,
。在 中
$1 == LastState
,请注意,awk
如果操作数看起来像数字,则将进行数字比较,否则将进行字符串比较6。例如,它会说100
、1e2
和100.0
是相同的。如果您想确保执行字符串比较,您可以执行$1 "" == LastState
,或者在分配时LastState
,使其LastState = $1 ""
记录它是一个字符串而不是可能的数字这一事实。您无需使用
LastState = $1
and检查两次是否相等LastState != $1
,而是可以next
在第一次检查操作的末尾添加一个,以跳过再次比较的步骤,或者在无条件运行的操作中使用if
/语句。else
sort
能够很好地根据第4 个字段7对输入进行排序,您不必将其移到前面。或者分解
print "\t" $1
并使其更清晰/更直接:¹ 对于那些不是 shell 内置命令或函数或别名(当为文字且未加引号时)的 shell 语法中的关键字之一,例如
while
,for
... (当为文字且未加引号时也是如此)。.
² 几十年前,在 中包含或空字符串并不罕见,这两个字符串都表示当前工作目录,即使在$PATH
最前面(!)也会遇到这种问题,但现在没有人会愚蠢到这么做。³ 基本上,这是要求 shell 将位置参数(脚本的参数)与第一个字符
$IFS
4连接起来,然后(由于缺少引号),再次在的任何字符上拆分结果$IFS
,并将每个结果单词进行globbing又名文件名生成又名路径名扩展。这$*
在 shell 中是有意义的rc
,shell 曾经是 Bourne shell 的后继者,尽管不幸的是从未发生过,但您需要一个#! /bin/rc
shebang,或者zsh
在不在/模拟$*
中运行时扩展到非空位置参数。sh
ksh
4使用 Bourne shell(一种
/bin/sh
1997 年时仍可在某些系统上找到的预 POSIX shell),它会用空格将它们连接起来,而不管 的值如何$IFS
。5
cat
没有这些问题,但是(这也适用于sort
和awk
)将-
其视为标准输入,因此如果您有一个名为的文件-
,您必须将其作为./
或任何其他路径传递给它以解决这个问题。6在许多
awk
实现中,这甚至不是字节到字节的字符串比较,而是两个字符串的排序是否相同。awk
在 C 语言环境中运行将确保这是字符串比较。7该
-k start,end[flag]
语法是新的(如 90 年代初期,也许是 80 年代末)语法,可能在 1997 年仍然有sort
实现不支持它,只有古老的现在早已过时的+offset
语法,这也许可以解释为什么他们不使用它。是的。如果您要这样调用脚本,则需要将其标记为可执行(最好有一个 shbang)。这不是什么新鲜事。
您的术语“点斜杠”只是指定脚本的路径,单个点代表当前目录。正如@ilkachu 指出的那样,在路径中包含“点”是一个安全问题,这会导致当前可能存在的任何目录
$CWD
出现在您的搜索路径中。