西施

西施

我想将表达式的结果(即命令的输出)分配给变量,然后对其进行操作 - 例如,将其与字符串连接,然后回显它。这是我所得到的:

#!/bin/bash
cd ~/Desktop;
thefile= ls -t -U | grep -m 1 "Screen Shot";
echo "Most recent screenshot is: "$thefile;

但这输出:

Screen Shot 2011-07-03 at 1.55.43 PM.png
Most recent screenshot is: 

因此,看起来它没有被分配给$thefile,而是在执行时被打印。

我缺少什么?

答案1

shell 赋值是一个单词,等号后没有空格。所以你写的内容为 ; 分配了一个空值thefile。此外,由于赋值与命令分组,因此它会创建thefile一个环境变量,并且赋值对于该特定命令来说是本地的,即只有调用才能ls看到分配的值。

您想要捕获命令的输出,因此您需要使用命令替换:

thefile=$(ls -t -U | grep -m 1 "Screen Shot")

(一些文献显示了另一种语法thefile=`ls …`;反引号语法等同于美元括号语法,只是有时在反引号内引用很奇怪,所以只需使用$(…)。)

关于您的脚本的其他备注:

  • 组合-t(按时间排序)与-U(不与 GNU 排序ls)没有意义;只需使用-t.

  • 与其使用grep匹配屏幕截图,不如传递通配符ls并用于head捕获第一个文件更清晰:

      thefile=$(ls -td -- *"Screen Shot"* | head -n 1)
    
  • 一般是一个解析输出的坏主意ls。如果您的文件名包含不可打印字符,这可能会严重失败。但是,如果没有 ,按日期对文件进行排序是很困难的ls,因此如果您知道文件名中不会有不可打印的字符或反斜杠,那么这是一个可以接受的解决方案。

  • 始终在变量替换周围使用双引号,即这里写

      echo "Most recent screenshot is: $thefile"
    

    如果没有双引号,变量的值将被重新扩展,如果它包含空格或其他特殊字符,这将导致麻烦。

  • 行尾不需要分号。它们是多余的但无害的。

  • 在 shell 脚本中,通常最好包含set -e。如果任何命令失败(通过返回非零状态),这会告诉 shell 退出。

如果您有 GNU findand sort(特别是如果您运行的是非嵌入式 Linux 或 Cygwin),则还有另一种方法来查找最新文件:列出find文件及其日期,然后使用sortand read(这里假设bashzsh用于-d ''读取NUL 分隔的记录)来提取最新的文件。

IFS=/ read -rd '' ignored thefile < <(
  find -maxdepth 1 -type f -name "*Screen Shot*" -printf "%T@/%p\0" |
    sort -rnz)

如果您愿意在 zsh 而不是 bash 中编写此脚本,那么有一种更简单的方法来捕获最新文件,因为 zsh 有全局限定符不仅允许通配符匹配名称,还允许匹配文件元数据。(om[1])模式后面的部分是 glob 限定符;om按增加年龄(即按修改时间,最新的在前)对匹配项进行排序,并[1]仅提取第一个匹配项。整个匹配项需要放在括号中,因为它在技术上是一个数组,因为 globbing 返回一个文件列表,即使这[1]意味着在这种特殊情况下该列表包含(最多)一个文件。

#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
print -r "Most recent screenshot is: $thefile"

答案2

如果您想使用多行/多个命令来执行此操作,则可以执行以下操作:

output=$( bash <<EOF
#multiline/multiple command/s
EOF
)

或者:

output=$(
#multiline/multiple command/s
)

例子:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"

输出:

first
second
third

答案3

为了完整起见,在不同的 shell 中:

伯恩外壳

70 年代末的 Bourne shell 引入了命令替换。现在它已不再使用,但许多 shell 的语法都基于该 shell 的语法。

var=`any shell code`

在子进程中解释any shell code,然后输出将进入管道。同时,shell 从管道的另一端读取输出并将其存储在$var变量中。

但请注意全部该输出中的尾随换行符将被删除。

Bourne shell 不支持数组变量,但您可以使用以下命令将命令输出的单词存储到位置参数($1, $2...)中:

set -- `some code`

或者:

set x `some code`; shift

在早期版本中--不支持。

, 除了尾部换行符剥离之外, 的输出some code也受到基于$IFS- 的分割 + 通配符的影响。例如,如果某些代码输出foo,/*<newline><newline>$IFS包含,$1则将包含/foo,其余参数是 中的所有非隐藏文件/

西施

同样是从 70 年代末到 80 年代初,当时非常流行,tcsh尽管有许多缺点,但仍然在某些系统上幸存下来。

set array = `some code`

将输出拆分some code(按空格、制表符或换行符)的结果单词存储在$array列表变量中。

set array = "`some code`"

相同,只是仅在换行符上进行分割( 的元素$array将是输出的非空行)。

ksh88 和 POSIX 等 shell。

如今,sh标准 sh 语言的解释器的一种或多种实现,它本身主要基于 ksh88 的子集,ksh88 是 David Korn 编写的 shell 的最后一个演变,衍生自 Bourne shell,并进行了一些修复和增强功能。其中包括 bash、dash、ksh、yash、mksh、zsh(尽管对于其中许多,必须通过某些选项启用对 sh 规范的遵从,或者将它们称为sh)。

var=$(any shell code)

它的工作原理类似于 Bourne shell var=`...`,只不过它使嵌套变得更加容易,并且不会弄乱其中反斜杠的解释。

尽管 ksh 有数组,但 POSIX sh 规范中并未包含数组支持。

rc 和导数

rc是 80 年代末 Research Unix V10 和 plan9 的外壳,曾经是 Unix 的后继者。还存在该 shell 的公共领域克隆,它产生了一些衍生品:至少esakanga这样。它的语法比上面的要好得多,但不幸的是从未真正流行起来。

array = `cmd
array = ` {any shell code}

存储 的输出any shell code,将 的字符拆分$ifs$array

array = `` (chars) {any shell code}

相同,只是chars使用指定的值而不是$ifs进行拆分。使用var = ``(){shell code},可以准确地存储输出,无需拆分(唯一可以真正开箱即用地执行此操作的 shell)。

克什

ksh 是 80 年代初第一个引入数组的类 Bourne shell。语法是:

set -A array -- $(some code)

与 Bourne shell 一样,它执行尾随换行符剥离、$IFS-分割和通配符操作。

some command | read var1 var2 var3

还可以用于读取 输出的第一行some command,将其拆分$IFS(但可以使用反斜杠来转义分隔符和换行符)并将结果存储在这些变量中。

它也适用于 zsh,但不适用于其他一些类似 ksh 的 shell,例如 bash、yash 或 pdksh/mksh,它们read在子 shell 中运行(对于sh,标准未指定它是否发生,因此您不能在 ) 中可移植地使用它sh。在 bash 中,shopt -s lastpipe有助于 shell 的非交互式调用。

some code | read -A array

相同,除了第一行的单词存储在数组元素中(bash将选项重命名-a为其自己的read内置选项)。

ksh 还引入了协同进程。

some command |&
IFS= read -r first_line <&p
IFS= read -r second_line <&p

例如,可用于将输出中的第一行和第二行读取some command到各自的变量中。

zsh 和 bash 后来还使用不同的语法添加了协同进程支持。

桀骜

zsh(另一个类似 Bourne 的 shell,但也具有 csh 和 rc 的功能)在 1991 年的 2.0 版本中引入了数组支持(前一年发布第一个版本几个月后)。

array=( $(some code) )

将执行换行符剥离和 IFS 分割(不是通配符)并将结果字分配给数组的元素。

该语法后来被 1993 年的 ksh93 和 1996 年的 bash 2.0 复制,尽管它们像 ksh88 一样在顶部进行通配。

$IFS与in以外的事物分开zsh

array=( ${(f)"$(some code)"} ) # split on lineFeed aka newline
array=( ${(0)"$(some code)"} ) # split on NUL
array=( ${(s[string])"$(some code)"} ) # split on any string

zsh也是唯一可以在其变量中存储 NUL 字符的 shell,因此是唯一不会因包含此类字符的命令的输出而阻塞的 shell。 NUL 实际上是$IFSin的默认值zsh(除了空格、制表符和换行符之外,就像其他类似 Bourne 的 shell 中一样)。

some command | IFS= read -rd '' var

some command可用于将最多第一个 NUL 字符的输出存储到 中$var。由于 NUL 不能出现在文本中,因此这是一种按原样存储文本的方法。-d来自 ksh93,但-d ''对 NUL 进行定界是 bash/zsh 的补充。

zsh 还添加了一些更多选项,read例如-k读取任意数量的字符(最初旨在读取按键,但与-u指定要读取的 fd 结合使用时可用于任意字符)。ksh93后来bash添加了-N/-n具有相关语义的选项:

some command | read -u0 -k10 var

some command存储in输出的前 10 个字符$var

sysread模块中的构建还可zsh/system用于读取块中的输入,作为read()系统调用的原始接口。

同名模块中的内置函数zpty也可用于通过伪终端对与命令进行交互,从而允许您以更具交互性的方式发送输入并读取其输出expect

克什93

由其作者于 1993 年末发布的对 ksh 的完全重写,它极大地启发了 bash 2+(以及在某种程度上 zsh 和 mksh)

var=${
  any shell code
}

var=$(any shell code)这与except不在子 shell 环境中运行相同any shell code,这意味着它的效率稍高一些,因为 shell 不需要相同并在之后恢复环境,并且意味着您在其中所做的修改之后仍然存在。

最新版本的mksh.

巴什

readarray array < <(some command)

将行some command(包括可以使用选项删除的分隔符-t)存储在数组的元素中$array<(...)所谓的进程替换在 80 年代中期被添加到 ksh 中,但最初不能用作重定向的目标(很久以后在 ksh93 中解决了)。

在 bash 4.4+ 中,您可以使用除换行符之外的不同分隔符,包括 NUL:

readarray -t -d '' array < <(find . -print0)

find例如,可用于将的输出中以 NUL 分隔的记录的内容存储到$array.

您还可以这样做:

read var1 var2 < <(some command)

即使lastpipe未启用该选项,它也可以工作(在 zsh 中也可以工作)。

亚什

read var1 var2 <(any shell code)

虽然看起来像 ksh 的进程替换,但其中的功能称为进程重定向,并且不会扩展到某个命名管道的路径,而是将相应的 fd(此处为 0)分配给另一端连接到的管道的读取端命令的输出。

相对较晚的出现者,其语法与其他 shell 显着不同(通常要好得多,尽管目标仍然在变化):

set array (some shell code)

将 shell 代码输出的每一行内容分配给数组的元素。

some command | read var1 var2

将读取第一行,分割$IFS(尽管会改变)或任意分隔符-d delim并存储在$var1,中$var2read -a array将该行的单词存储在数组中。

some command | read -L var1 var2

对于不同变量中的前两行。

some command | read -z var

对于第一个 NUL 之前的所有内容(因此文本输出的所有内容)都存储在$var.

相关内容