我有一系列的路径:
paths=(
/foo/exists1
/foo/exists2
/foo/missing1
)
要找到丢失的内容:
ls "${paths[@]}" 1>/dev/null
演出:
ls:无法访问‘/foo/missing1’:没有此文件或目录
很好。现在我想清理一下:
ls "${paths[@]}" 1>/dev/null | sed 's/ls: cannot access //' | sed 's/: No such file or directory//''
但我明白:
ls:无法访问'/foo/missing1':没有这样的文件或目录
/foo/exists1
/foo/exists2
因此sed
不起作用,并且现有文件也会显示出来。
为什么会发生这种情况(为什么它会忽略1>/dev/null
),以及我该如何解决这个问题?
答案1
你已经已经发现代码没有按预期运行的直接原因是:错误ls
被报告到 stderr(如POSIX 建议),它不会被管道捕获为输入。因此,您得到的是正常输出 (通过您的语句不变sed
) 和 stderr (绕过它们) 的混合。我不知道为什么您的ls
输出在调用之间发生了变化;将 stdout 重定向到 /dev/null 应该会从输出中删除所有“正常”(现有路径)。对此的修复是不是但是将 stderr 推送到 stdout。
如果你想要一个可靠的脚本,那么对输出进行后处理ls
是一个危险的想法。关于这个主题的一篇好文章是“为什么你不应该解析 ls(1) 的输出”,可在 wooledge.org 网站上找到。Unix & Linux 网站上有一个深入的问答,涉及一些问题:为什么不是解析ls
(以及该做什么)?。结果是 UNIX 文件名几乎可以包含任何字符,包括空格、制表符、换行符、单引号、双引号、转义单引号等!举几个简单的例子,考虑一下这些名称的目录,它们都是完全合法的:
- “没有这样的文件” (
mkdir "No such file"
) - “ls:无法访问‘foo’:没有那个文件或目录”(
mkdir "ls: cannot access 'foo': No such file or directory"
) “目录
和
嵌入
换行符”(
mkdir $'directory\nwith\nembedded\newlines'
)
第一个目录是被错误捕获的(从 stdout 中)grep
。第二个目录也是被错误捕获的,但随后被进一步破坏为完全不同的道路-- 可能存在也可能不存在! -- 语句sed
。第三个是将 的输出传递ls
到面向行的程序时发生的情况的一个例子;如果目录不存在,ls
则会在多行上说明,这可能是您最终得到两个独立sed
语句的原因!
为了区分“好路径”(存在且可读的路径)和“坏路径”,我建议循环遍历数组并为每个路径构建新数组。
for p in "${paths[@]}"
do
if [ -r "$p" ]
then
goodpaths+=("$p")
else
badpaths+=("$p")
fi
done
然后你可以对每一组做任何你想做的事情:
printf 'Good path: -->%s<--\n' "${goodpaths[@]}"
echo
printf 'Bad path: -->%s<--\n' "${badpaths[@]}"
答案2
问题显而易见......错误转到文件描述符 2,而不是标准输出。
因此必须重定向2
到1
然后 grep:
paths=(
/foo/exists1
/foo/exists2
/foo/missing1
)
ls "${paths[@]}" 2>&1 \
| grep 'No such file' \
| sed 's/ls: cannot access '\''//' \
| sed 's/'\'': No such file or directory//'