如何找出命令是否以 POSIX 兼容的方式存在?
How to find out if a command exists in a POSIX compliant manner?
请参阅 . It describes that type
as well as command -v
option is optional in POSIX.1-2004 上的讨论。
答案在 Check if a program exists from a Bash script doesn't help either. Just like type
, hash
is also marked as XSI in POSIX.1-2004. See http://pubs.opengroup.org/onlinepubs/009695399/utilities/hash.html 处标记为正确。
那么什么是 POSIX 兼容的方式来编写 shell 脚本来查找系统上是否存在命令?
你想怎么做?您可以在 $PATH
的当前值中查找目录上的命令;您可以查看系统 PATH 默认指定的目录(getconf PATH
只要 getconf
存在于路径上)。
您打算使用哪种实现语言? (例如:我有一个 Perl 实现,可以在 $PATH
上找到可执行文件,但 Perl 不是 POSIX 的一部分;它与你有很大关系吗?)
为什么不直接尝试 运行ning 呢?如果您要处理基于 Busybox 的系统,则无法通过搜索找到许多可执行文件 — 它们内置于 shell 中。主要的警告是如果一个命令在 运行 没有参数时做了一些危险的事情——但是很少有 POSIX 命令,如果有的话,这样做。您可能还需要确定哪些命令退出状态指示未找到该命令,哪些命令反对不使用适当的参数调用。并且几乎不能保证所有系统在这方面都保持一致。这是一个令人担忧的过程,以防你没有聚集。
Perl 实现 pathfile
#!/usr/bin/env perl
#
# @(#)$Id: pathfile.pl,v 3.4 2015/10/16 19:39:23 jleffler Exp $
#
# Which command is executed
# Loosely based on 'which' from Kernighan & Pike "The UNIX Programming Environment"
#use v5.10.0; # Uses // defined-or operator; not in Perl 5.8.x
use strict;
use warnings;
use Getopt::Std;
use Cwd 'realpath';
use File::Basename;
my $arg0 = basename([=10=], '.pl');
my $usestr = "Usage: $arg0 [-AafhqrsVwx] [-p path] command ...\n";
my $hlpstr = <<EOS;
-A Absolute pathname (determined by realpath)
-a Print all possible matches
-f Print names of files (as opposed to symlinks, directories, etc)
-h Print this help message and exit
-q Quiet mode (don't print messages about files not found)
-r Print names of files that are readable
-s Print names of files that are not empty
-V Print version information and exit
-w Print names of files that are writable
-x Print names of files that are executable
-p path Use PATH
EOS
sub usage
{
print STDERR $usestr;
exit 1;
}
sub help
{
print $usestr;
print $hlpstr;
exit 0;
}
sub version
{
my $version = 'PATHFILE Version $Revision: 3.4 $ ($Date: 2015/10/16 19:39:23 $)';
# Beware of RCS hacking at RCS keywords!
# Convert date field to ISO 8601 (ISO 9075) notation
$version =~ s%$(Date:) (\d\d\d\d)/(\d\d)/(\d\d) (\d\d:\d\d:\d\d) $%$ -- $%go;
# Remove keywords
$version =~ s/$([A-Z][a-z]+|RCSfile): ([^$]+) $//go;
print "$version\n";
exit 0;
}
my %opts;
usage unless getopts('AafhqrsVwxp:', \%opts);
version if ($opts{V});
help if ($opts{h});
usage unless scalar(@ARGV);
# Establish test and generate test subroutine.
my $chk = 0;
my $test = "-x";
my $optlist = "";
foreach my $opt ('f', 'r', 's', 'w', 'x')
{
if ($opts{$opt})
{
$chk++;
$test = "-$opt";
$optlist .= " -$opt";
}
}
if ($chk > 1)
{
$optlist =~ s/^ //;
$optlist =~ s/ /, /g;
print STDERR "$arg0: mutually exclusive arguments ($optlist) given\n";
usage;
}
my $chk_ref = eval "sub { my($cmd) = \@_; return -f $cmd && $test $cmd; }";
my @PATHDIRS;
my %pathdirs;
my $path = defined($opts{p}) ? $opts{p} : $ENV{PATH};
#foreach my $element (split /:/, $opts{p} // $ENV{PATH})
foreach my $element (split /:/, $path)
{
$element = "." if $element eq "";
push @PATHDIRS, $element if $pathdirs{$element}++ == 0;
}
my $estat = 0;
CMD:
foreach my $cmd (@ARGV)
{
if ($cmd =~ m%/%)
{
if (&$chk_ref($cmd))
{
print "$cmd\n" unless $opts{q};
next CMD;
}
print STDERR "$arg0: $cmd: not found\n" unless $opts{q};
$estat = 1;
}
else
{
my $found = 0;
foreach my $directory (@PATHDIRS)
{
my $file = "$directory/$cmd";
if (&$chk_ref($file))
{
$file = realpath($file) if $opts{A};
print "$file\n" unless $opts{q};
next CMD unless defined($opts{a});
$found = 1;
}
}
print STDERR "$arg0: $cmd: not found\n" unless $found || $opts{q};
$estat = 1;
}
}
exit $estat;
请参阅 type
as well as command -v
option is optional in POSIX.1-2004 上的讨论。
答案在 Check if a program exists from a Bash script doesn't help either. Just like type
, hash
is also marked as XSI in POSIX.1-2004. See http://pubs.opengroup.org/onlinepubs/009695399/utilities/hash.html 处标记为正确。
那么什么是 POSIX 兼容的方式来编写 shell 脚本来查找系统上是否存在命令?
你想怎么做?您可以在 $PATH
的当前值中查找目录上的命令;您可以查看系统 PATH 默认指定的目录(getconf PATH
只要 getconf
存在于路径上)。
您打算使用哪种实现语言? (例如:我有一个 Perl 实现,可以在 $PATH
上找到可执行文件,但 Perl 不是 POSIX 的一部分;它与你有很大关系吗?)
为什么不直接尝试 运行ning 呢?如果您要处理基于 Busybox 的系统,则无法通过搜索找到许多可执行文件 — 它们内置于 shell 中。主要的警告是如果一个命令在 运行 没有参数时做了一些危险的事情——但是很少有 POSIX 命令,如果有的话,这样做。您可能还需要确定哪些命令退出状态指示未找到该命令,哪些命令反对不使用适当的参数调用。并且几乎不能保证所有系统在这方面都保持一致。这是一个令人担忧的过程,以防你没有聚集。
Perl 实现 pathfile
#!/usr/bin/env perl
#
# @(#)$Id: pathfile.pl,v 3.4 2015/10/16 19:39:23 jleffler Exp $
#
# Which command is executed
# Loosely based on 'which' from Kernighan & Pike "The UNIX Programming Environment"
#use v5.10.0; # Uses // defined-or operator; not in Perl 5.8.x
use strict;
use warnings;
use Getopt::Std;
use Cwd 'realpath';
use File::Basename;
my $arg0 = basename([=10=], '.pl');
my $usestr = "Usage: $arg0 [-AafhqrsVwx] [-p path] command ...\n";
my $hlpstr = <<EOS;
-A Absolute pathname (determined by realpath)
-a Print all possible matches
-f Print names of files (as opposed to symlinks, directories, etc)
-h Print this help message and exit
-q Quiet mode (don't print messages about files not found)
-r Print names of files that are readable
-s Print names of files that are not empty
-V Print version information and exit
-w Print names of files that are writable
-x Print names of files that are executable
-p path Use PATH
EOS
sub usage
{
print STDERR $usestr;
exit 1;
}
sub help
{
print $usestr;
print $hlpstr;
exit 0;
}
sub version
{
my $version = 'PATHFILE Version $Revision: 3.4 $ ($Date: 2015/10/16 19:39:23 $)';
# Beware of RCS hacking at RCS keywords!
# Convert date field to ISO 8601 (ISO 9075) notation
$version =~ s%$(Date:) (\d\d\d\d)/(\d\d)/(\d\d) (\d\d:\d\d:\d\d) $%$ -- $%go;
# Remove keywords
$version =~ s/$([A-Z][a-z]+|RCSfile): ([^$]+) $//go;
print "$version\n";
exit 0;
}
my %opts;
usage unless getopts('AafhqrsVwxp:', \%opts);
version if ($opts{V});
help if ($opts{h});
usage unless scalar(@ARGV);
# Establish test and generate test subroutine.
my $chk = 0;
my $test = "-x";
my $optlist = "";
foreach my $opt ('f', 'r', 's', 'w', 'x')
{
if ($opts{$opt})
{
$chk++;
$test = "-$opt";
$optlist .= " -$opt";
}
}
if ($chk > 1)
{
$optlist =~ s/^ //;
$optlist =~ s/ /, /g;
print STDERR "$arg0: mutually exclusive arguments ($optlist) given\n";
usage;
}
my $chk_ref = eval "sub { my($cmd) = \@_; return -f $cmd && $test $cmd; }";
my @PATHDIRS;
my %pathdirs;
my $path = defined($opts{p}) ? $opts{p} : $ENV{PATH};
#foreach my $element (split /:/, $opts{p} // $ENV{PATH})
foreach my $element (split /:/, $path)
{
$element = "." if $element eq "";
push @PATHDIRS, $element if $pathdirs{$element}++ == 0;
}
my $estat = 0;
CMD:
foreach my $cmd (@ARGV)
{
if ($cmd =~ m%/%)
{
if (&$chk_ref($cmd))
{
print "$cmd\n" unless $opts{q};
next CMD;
}
print STDERR "$arg0: $cmd: not found\n" unless $opts{q};
$estat = 1;
}
else
{
my $found = 0;
foreach my $directory (@PATHDIRS)
{
my $file = "$directory/$cmd";
if (&$chk_ref($file))
{
$file = realpath($file) if $opts{A};
print "$file\n" unless $opts{q};
next CMD unless defined($opts{a});
$found = 1;
}
}
print STDERR "$arg0: $cmd: not found\n" unless $found || $opts{q};
$estat = 1;
}
}
exit $estat;