Bash 如何在使用“ls”命令并将输出分配给变量时抑制stderr?

Bash 如何在使用“ls”命令并将输出分配给变量时抑制stderr?,bash,shell,scripting,ksh,Bash,Shell,Scripting,Ksh,我正在尝试创建一个脚本,该脚本将根据文件名和时间戳在服务器上搜索文件,并将这些文件计数到一个变量中。当文件可用时,它可以正常工作,但当文件不可用时抛出一个stderr 为了抑制stderr,我试图将其重定向到/dev/null,但即使这样也没有帮助,错误仍然显示在屏幕上。我知道我可以首先使用“if”语句检查文件是否可用,然后进行计数,但这将不必要地使脚本过长 那么,有没有一种方法可以在一行代码中将输出分配给变量的同时,获取文件计数并抑制stderr(如果有的话) 当服务器上存在名为“exampl

我正在尝试创建一个脚本,该脚本将根据文件名和时间戳在服务器上搜索文件,并将这些文件计数到一个变量中。当文件可用时,它可以正常工作,但当文件不可用时抛出一个stderr

为了抑制stderr,我试图将其重定向到/dev/null,但即使这样也没有帮助,错误仍然显示在屏幕上。我知道我可以首先使用“if”语句检查文件是否可用,然后进行计数,但这将不必要地使脚本过长

那么,有没有一种方法可以在一行代码中将输出分配给变量的同时,获取文件计数并抑制stderr(如果有的话)

当服务器上存在名为“example”的文件时,此命令将成功运行:

file_name=example
file_date=20190901

file_count=`ls -lrt "$PWD"/"$file_name"*"$file_date"* | wc -l`
但当文件不存在时,它会在屏幕上抛出一个stderr,如下所示:

ls: cannot access /home/saurap01/example*20190901*: No such file or directory
为了抑制此错误,我尝试将其重定向到/dev/null,如下所示:

file_count=`ls -lrt "$PWD"/"$file_name"*"$file_date"* | wc -l` > /dev/null 2>&1
但即使这样也无助于抑制错误

我是否可以使用以下命令来隐藏stderr:

file_count=`ls -lrt "$PWD"/"$file_name"*"$file_date"* | wc -l` 2>&-

如果没有正确重定向stderr,则2>/dev/null应该正好位于要抑制其stderr的命令之后,如:

file_count=`ls -lrt "$PWD"/"$file_name"*"$file_date"* 2>/dev/null | wc -l`
但是,对于这样的任务,您应该使用find。例如,使用GNU查找:

find -maxdepth 1 \
     -type f \
     -name "$file_name*$file_date*" \
     -printf '.' | wc -c
或使用任何符合POSIX的查找:

find . \
! \( -type d -path '*/*' -prune \) \
     -type f \
     -name "$file_name*$file_date*" \
     -exec printf '.%.s' {} + | wc -c

直接的解决办法是增加

2>/dev/null
在命令替换中

您的代码展示了许多反模式,所以让我们也来讨论这些

如中所述,shell在将参数传递给ls之前已经执行了通配符扩展。此外,您传递给ls的选项执行大量额外的工作;您忽略了大小、所有者等,但导致使用-l选项查找这些内容;您不关心排序顺序,但可以使用-rt强制按时间戳排序

此外,如中所述,ls命令具有许多用于人类可读输出的功能,这使得它不适合在脚本中使用

shell开箱即用,通常配置为globbing设置,以便在glob模式与任何文件不匹配时返回glob模式本身。在Bash而不是ksh中,可以使用

setopt -s nullglob
然后简单地打印文件

### BUG; see below
printf '%s\n' ./"$file_name"*"$file_date"* |
wc -l
但是,如果其中一个文件名包含换行符,则会产生错误的结果-然后,输出的行数将不同于文件数。一个简单的解决方法是避免printf,完全使用shell的特性

set -- ./"$file_name"*"$file_date"*
Thrs将简单地将通配符匹配分配给参数列表$1、$2等,因此

echo $#
将打印文件数

如果您没有Bash,因此没有shopt,那么可以通过检查第一个文件是否存在来查找glob是否扩展到自身

set -- ./"$file_name"*"$file_date"*
if [ -e "$1" ]; then
    echo "$#"
else
    echo 0
fi

除此之外,请注意$PWD很少有用。如果您需要将相对文件名转换为绝对文件名,或者出于其他原因需要知道当前目录的完整路径,它偶尔会有用;但是在这些场景之外,只需使用相对路径来引用当前目录中的内容。

为什么要使用-r和-t?请不要仅在附加标记的问题中使用shopt GNU bash,谢谢@米拉比洛斯:由于标签相互冲突或至少令人困惑,OP的目标是哪个外壳还不清楚;但是谢谢你的评论;我将专门将其标记为Bash-only。事实上,许多shell问题都标记为两者。在这种情况下,我通常使用子集,或者只使用POSIX sh,然后如果必要的话,将特定于shell的部分作为附加组件指出。谢谢你的考虑!避免nullglob的通常方法是测试第一个参数是否存在。我通常写一些东西,比如x in*;打破僵局;dosomethingwith*;打破只运行一次循环,ofc也可以使用&&。在这里,你只需测试-e$1就可以抓住它。@mirabilos说得对;我已经包括了一篇关于这个的短文。谢谢!使用重定向stderr对我有效。