bash 中 where cmdlet 的等价物是什么?

bash 中 where cmdlet 的等价物是什么?

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,,,test2lstest3感觉就像是这样调用的:

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 -print0xargs -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

相关内容