使用变量作为命令的输入

Using variables as the input to command

我搜索了各种留言板以了解为什么在某些情况下我不能使用变量作为命令的输入。它是 STDIN issue/limitation 吗?为什么使用 echo 和此处的字符串可以解决问题?

例如,

~$ myvar=$(ls -l)
~$ grep Jan "$myvar"
grep: total 9
-rwxr-xr-x 1 jvp jvp 561 Feb  2 23:59 directoryscript.sh
-rw-rw-rw- 1 jvp jvp   0 Jan 15 10:30 example1
drwxrwxrwx 2 jvp jvp   0 Jan 19 21:54 linuxtutorialwork
-rw-rw-rw- 1 jvp jvp   0 Jan 15 13:08 littlefile
-rw-rw-rw- 1 jvp jvp   0 Jan 19 21:54 man
drwxrwxrwx 2 jvp jvp   0 Feb  2 20:33 projectbackups
-rwxr-xr-x 1 jvp jvp 614 Feb  2 20:41 projectbackup.sh
drwxrwxrwx 2 jvp jvp   0 Feb  2 20:32 projects
-rw-rw-rw- 1 jvp jvp   0 Jan 19 21:54 test1
-rw-rw-rw- 1 jvp jvp   0 Jan 19 21:54 test2
-rw-rw-rw- 1 jvp jvp   0 Jan 19 21:54 test3: File name too long

如你所见,我得到了错误... 'File name too long'

现在,我可以使用以下任一方法来实现它:

  echo "$myvar" | grep Jan  

  grep Jan <<< "$myvar"

但是,我真的很想更好地理解为什么会这样。也许我遗漏了一些关于命令替换的基础知识或者什么是可接受的 STDIN 形式。

Grep 将命令行参数文件作为命令行参数,而不是直接的字符串。您确实需要 echo 在您的变量中进行 grep 搜索。

我认为这里存在对 Unix tools/Bash 运作方式的根本误解。

您在这里尝试做的似乎是将 ls 的输出存储在一个变量中(出于其他原因您不应该这样做)并尝试 grep使用 grep.

存储在该变量中的字符串

这不是 grep 的工作方式。如果您查看 man page for grep,它表示:

SYNOPSIS

   grep [OPTIONS] PATTERN [FILE...]
   grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]

DESCRIPTION

   grep searches the named input FILEs for lines containing a match to
   the given PATTERN.  If no files are specified, or if the file “-” is
   given, grep searches standard input.  By default, grep prints the
   matching lines.

请注意,它明确表示“grep 搜索命名输入 FILEs”。 然后它继续说“如果没有指定文件[...] grep 搜索标准输入”。

换句话说,根据定义 grep 不搜索字符串。它搜索文件。因此,您不能通过 bash 变量传递 grep 字符串。

当您输入时

grep Jan "$myvar"

根据语法,grep 认为 "Jan" 是 PATTERN,而 "$myvar" 中的 entire 字符串是一个 FILEname。因此错误 File name too long.


当你写

echo "$myvar" | grep Jan

您现在正在做的是bash将“$myvar”的内容输出到标准输出。 bash 中的 |(管道运算符)将 echo 命令的 stdout(标准输出)连接到 grep 命令的 stdin(标准输入)。如上所述,当您省略 grep 的 FILEname 参数时,默认情况下它会在 stdin 中搜索字符串,这就是它起作用的原因。

grep 实用程序可以运行...

  1. 在命令行提供名称的文件上,在用于匹配的正则表达式之后
  2. 在其标准输入上提供的流上。

您正在这样做:

myvar=$(ls -l)
grep Jan "$myvar"

这将变量 myvar 的内容作为参数提供给 grep 命令,由于它不是文件名,因此不起作用。

有很多方法可以实现您的目标。这里有几个例子。

使用变量的内容作为连接到 grep 的标准输入的流,使用以下方法之一(都提供相同的输出):

grep Jan <<<"$myvar" 
echo "$myvar" | grep Jan
grep Jan < <(echo "$myvar")

避免以变量开头,将ls的输出直接发送给grep:

ls -l | grep Jan
grep Jan < <(ls -l)

提供 grep 实际上是文件名的表达式:

grep Jan <(ls -l)

<(ls -l) 表达式是导致创建 FIFO (first-in first-out) 特殊文件的语法。 ls -l 命令将其输出发送到 FIFO。表达式由 Bash 转换为可用于读取的实际文件名。

为了消除任何混淆,下面的两个陈述(已在上面显示)看起来很相似,但根本上有很大不同:

grep Jan <(ls -l)
grep Jan < <(ls -l)

在第一个中,grep 接收一个文件名作为参数并读取该文件。在第二种情况下,额外的 <(两个 < 之间的空格很重要)创建一个重定向,读取 FIFO 并将其输出提供给 grep 的标准输入。两种情况下都有一个 FIFO,但它以完全不同的方式呈现给命令。