![bash 中 where cmdlet 的等价物是什么?](https://linux22.com/image/75763/bash%20%E4%B8%AD%20where%20cmdlet%20%E7%9A%84%E7%AD%89%E4%BB%B7%E7%89%A9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.png)
where cmdlet 是我在 PS 中经常使用的东西 - 它非常方便。
如果我想获取名称以“test”开头的所有文件,我会这样做:
ls | ?{$_.name -like 'test*'}
nix 管理员在 bash 中执行此操作的同样简洁的方法是什么? Bash 显然是文本而不是基于对象的,所以命令可能类似于:
ls | ?{<column#> -like 'test*'}
我不是在询问 ls 命令(ls test*
可以工作,但这是特定于命令的),我正在寻找一个简洁的 bash,相当于如何通过这样的过滤器传输输出。
答案1
长话短说:如果你不喜欢那个ls
东西,即使那是不是特定于命令(如下所述),使用arr=(); for foo in * [!*]*; do [[ $foo == test* ]] && arr+=("$foo"); done
.这基本上是arr=list_files.filter((i) -> (i.glob_match('test*'));
你用其他一些语言编写的。
但如果你继续阅读,你仍然会发现这ls
很好,并且应该通过直接数组赋值来跳过这些测试arr=(test*)
(mikeserv 让我意识到我真的需要先说这一点)。
文本过滤的快速回顾
就像其他人在评论中提到的那样,Unix shell 只是通过管道传输文本流,而不是对象包。由于 Unix 世界允许在文件名和许多其他空间中包含比 Windows 更多的内容,因此只有 shell 字边界是安全的。对于像文件名这样\0
幸运的是不允许的东西,您也可以在流中使用它。
grep
和朋友继续充当基本的文本过滤器。它吃在线从标准输入或文件中,与给定的正则表达式匹配并给出匹配的(或相反的,-v
)行或部分(-o
)作为标准输出,又一个使用起来仍然不安全的流。
这适用于以换行符分隔的文本,例如大多数代码和诗歌,但不适用于文件名。文件名可以包含换行符,并且grep
一个文件可能会看到两个单独的行ls
。
Unix shell 中的程序中的通配符是什么样的
在 UNIX shell 中,通配符由 shell 处理,而不是由目标程序处理。这使得事情变得更加一致,同时也会引起混乱,让您认为该test*
部分与 相关ls
。
假设我们有文件test1
,,,test2
lstest3
感觉就像是这样调用的:
ls test1 test2 test3
它基本上不知道你对它做了什么。
在内部,test*
扩展为 shell字。既然我们有了 struct for varname in [word-list]; do [commands]; done
,我们就可以做出这样的事情:
for i in *; do
if i matches the pattern; then
do something
fi # that marks endif
done
bash 中有一种东西[[
可以执行模式匹配。对于给定的结构[[ lhs == rhs ]]
,bash 检查是否lhs
与 上的模式匹配rhs
。在我们的例子中,我们可以使用[[ $i == test* ]]
该i matches the pattern
部分。这在最小的 POSIX shell 中不可用,请使用case
它。
我们需要添加一些动作do something
。在bash中,有数组:
# Arrays doesn't exist in POSIX standard too.
a=() # an empty array, since bash is weak-typed this doesn't mean anything
b=(foo bar baz) # array are assigned with, well, list-of-words.
for i in *; do
if [ $i == test* ]]; then
a+=("$i") # quoting avoids some word-splitting and you know what += is
fi # that marks endif
done
# and here do something to your lonely array, like those described in
# gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
这里还有一件事。在 Unix 系统中,按照惯例,以 开头的文件名.
标记隐藏文件,并且*
不会包含它们。.*
显式添加它们,但.
表示当前目录并..
表示basedir(与Windows相同),因此您想删除它们。使用.[!.]*
作品,因为它意味着一个点、一个非点的字符以及包括零个字符在内的任何数量。这与你的test*
事情无关,因为test
不以 开头.
。
对于多个模式“或”,我们可以使用extglob
(嗯,shopt -s extglob
首先运行)事物,或者我们可以用允许多个模式的地方@(patt1|patt2)
包装整个事物,...case
但为什么你需要这样的东西呢?只需使用a=(test*)
.test*
如果有匹配的话,会给你一个单词列表。对于多种模式,请使用a=(test* tset*)
.
但是如果全局匹配失败怎么办?
如果 glob 不匹配任何内容,则它会在单词列表中保留为 glob 模式本身。这对于懒惰的 shell 用户来说看起来不错,但对于像我们这样认真的脚本编写者来说却不是。
shopt
幸运的是, bash 中有一个名为 的nullglob
,它不会恢复 glob 模式。只需先使用shopt -s nullglob
它即可启用它。如果您希望它可以在其他 POSIX shell 中移植,那么让我们回到 for-and-case 过滤。
人们如何获得该ls
解决方案
--
如果您要通配的内容不是test*
but ,则添加以下内容*test*
,并且您可能会得到一些文件名,例如-test123
并被识别为某些ls
选项。--
标记选项结束按照惯例。
# So what you need is globbing:
echo test*
# But those are not clear enough since echo only adds spaces between them. Use newlines:
printf '%s\n' test*
# But our screen doesn't have so many lines. Ah, yes, ls can make it into columns:
ls -- test*
# But it lists the contents of the directory test233/. Let's ask it not to:
ls -d -- test*
# Oh, good enough, let's add some color and some pretty type indicator:
ls -dF --color=auto -- test*
因此,对于您的简单问题,它是这样的:ls -d test*
。但它是不是对于单个命令;我们ls
仅用于漂亮的打印。
那么管道中的文件名总是愚蠢的吗?
\0
不。有些程序试图通过添加/使用分隔符(例如find -print0
和xargs -0
)来让人们认为它们可以安全使用。不幸的是它需要一些技巧让贝壳接受\0
,所以..
在许多 shell 中仍然有更好的通配符解决方案。find
递归地遍历目录并有条件地打印找到的文件名,它经常被用来递归地列出文件。在 bash 中,我们有这样的:
shopt -s globstar
for i in **; do
try-some-test || continue
do-something
done
这工作做得很好。
但bash
并不能提供一切,例如,它没有整齐的并行命令,而且它永远不会像本机代码运行那么快。这就是为什么人们使用其他程序来parallel
做事。既然我们有\0
,那么,它并没有那么糟糕,我们可以用来find -print0
喂养它。
答案2
这将仅返回以“test”开头的文件:
ls | grep '^test'
更多示例:
这将仅返回包含单词“test”的行。我使用 ls -al" 而不是单独使用 "ls" 来获得每条记录结果 1 行:
ls -al | grep "test"
这将返回包含 test 或 word2 的任何行:
ls -al | grep "test\|word2"
这将返回任何不包含字符串“test”的行:
ls -al | grep -v "test"
如果你真的想按列进行操作,你可以这样做:
ls -al | awk '$9 ~ /^test/'
其中 $9 表示第 9 列,它指的是我的 Debian 机器上的文件名,下面的正则表达式表示它以“test”开头。 awk 通过任何空格和/或制表符和/或换行符来分隔列,这些空格和/或制表符和/或换行符被视为字段分隔符。
答案3
如果你想beginning
用“test”过滤掉所有文件(或更复杂),你可以使用egrep来应用正则表达式模式匹配。
在 ls 输出中搜索以该字母开头的文件D
$ ls |egrep "^D"
Desktop
Documents
Downloads
Dropbox
如果你不使用正则表达式,你会得到这个
$ ls |grep D
Desktop
Documents
Downloads
Dropbox
README
Vuze Downloads