我想将表达式的结果(即命令的输出)分配给变量,然后对其进行操作 - 例如,将其与字符串连接,然后回显它。这是我所得到的:
#!/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 find
and sort
(特别是如果您运行的是非嵌入式 Linux 或 Cygwin),则还有另一种方法来查找最新文件:列出find
文件及其日期,然后使用sort
and read
(这里假设bash
或zsh
用于-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 的公共领域克隆,它产生了一些衍生品:至少es
是akanga
这样。它的语法比上面的要好得多,但不幸的是从未真正流行起来。
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 实际上是$IFS
in的默认值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
,中$var2
。read -a array
将该行的单词存储在数组中。
some command | read -L var1 var2
对于不同变量中的前两行。
some command | read -z var
对于第一个 NUL 之前的所有内容(因此文本输出的所有内容)都存储在$var
.