【问题标题】:Using variables as the input to command使用变量作为命令的输入
【发布时间】:2017-02-05 15:57:32
【问题描述】:

我搜索了各种留言板以了解为什么在某些情况下我不能将变量用作命令的输入。这是一个 STDIN 问题/限制吗?为什么使用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

如您所见,我收到错误消息...“文件名太长”

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

  echo "$myvar" | grep Jan  

  grep Jan <<< "$myvar"

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

【问题讨论】:

  • 我认为这里没有问题。您的目录中似乎有一个名为 test3: File name too long 的文件。顺便说一句,以编程方式使用ls 输出从未被认为是一种好的做法。如果您有兴趣查找自特定时间以来已修改的文件,还有其他命令,例如 find
  • @codeforester 不,目录下没有这个文件,只是正确提出的错误信息的格式令人困惑。
  • 如果它是一个错误,它不会结束 OP 的变量,因为 stderr 没有被分配给myvar
  • 当您收到该错误消息时所看到的不是 grep 输出,而是 一条错误消息,实际上这里看起来像一个文件列表.但事实并非如此。 grep 将整个输入(ls 命令的输出)解释为单个文件名参数 以进行 grep。该文件名太长以至于引发错误。这里没有魔法。问题是错误消息再次包含建议的文件名。 看起来像是一个文件列表,所以是一个 grep 输出。但正如所说,事实并非如此。检查自己:使用超过 20 行的任意字符串运行 grep 命令。

标签: linux bash variables stdin


【解决方案1】:

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)

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

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

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

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

【讨论】:

  • 谢谢,我现在可以说我对这些基本概念有了更好的理解。我不清楚该变量提供的“字符串”既不是文件也不是通过“标准输入”流提供的。我想大多数命令都是这种情况?另外,您能否举个例子或澄清一下 FIFO 的两个不同版本,为什么要使用一个与另一个?
【解决方案2】:

我认为这里对 Unix 工具/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 字符串是一个文件名。因此出现错误File name too long


当你写作时

echo "$myvar" | grep Jan

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-11
      • 2017-02-07
      • 1970-01-01
      • 2013-02-07
      • 2011-12-04
      • 2019-05-01
      • 1970-01-01
      • 2021-01-18
      相关资源
      最近更新 更多