关于bash:如何从sh文件中获取值?

How to get value from file in sh?

本问题已经有最佳答案,请猛点这里访问。

建立:

文件a包含:

1
22

文件b包含:

1
12

我有shell脚本1.sh

1
2
3
4
5
#!/bin/sh

a=$(< a)
b=$(< b)
echo $(($a*$b)) > c

该脚本应从文件ab获取值,将它们相乘*,然后保存到文件c
但是在设置权限$ chmod a+rx 1.sh并运行它$ ./1.sh后,它会返回错误:

1
./1.sh: 5: ./1.sh: arithmetic expression: expecting primary:"*"

发生此错误是因为变量$a$b未获取值格式文件ab

  • 如果我echo $aecho $b则不返回任何内容;
  • 如果我在脚本中定义a=22b=12值,它可以工作;
  • 我还尝试了其他方法来获取a=$(< 'a')a=$(<"a")a=$(<"~/a")甚至a=$(< cat a)等文件的内容。 这些都没有奏效。

剧情转折:

但是,如果我将shebang行更改为#!/bin/bash以便使用Bash shell - 它可以工作。

题:

如何正确地从文件中获取数据?


忽略文件a和b中的所有内容但是数字:

1
2
3
4
5
#!/bin/sh

a=$(tr -cd 0-9 < a)
b=$(tr -cd 0-9 < b)
echo $(($a*$b))

见:man tr


如果你正在寻找"真正的"Bourne-Shell兼容性,而不是Bash的仿真,那么你必须去上学:

1
2
3
4
#!/bin/sh
a=`cat a`
b=`cat b`
expr $a \* $b > c

我在macOS和Linux(FC26)上尝试了#!/bin/sh下的原始示例,并且它表现正常,假设ab具有UNIX行结尾。如果无法保证,并且你需要在#!/bin/sh下运行(由bash模拟),那么这样的东西将起作用:

1
2
3
4
#!/bin/sh
a=$(<a)
b=$(<b)
echo $(( ${a%%[^0-9]*} * ${b%%[^0-9]*} )) > c


有很多方法。一种显而易见的方法是通过命令替换来管道子进程:

1
2
3
4
A=$(cat fileA.txt)   # 22
B=$(cat fileB.txt)   # 12
echo $((A*B))
# <do it in your head!>

如果多行有任何其他问题,您需要研究如何使用Bash变量$IFS(内部文件分隔符)。通常IFS定义为:IFS=$' \t
'
,因此如果您需要能够可靠地读取Windows和Linux EOL的行结尾,则可能需要对其进行修改。

附录:

Process Substitution

Bash, Zsh, and AT&T ksh{88,93} (but not pdksh/mksh) support process
substitution. Process substitution isn't specified by POSIX. You may
use NamedPipes to accomplish the same things. Coprocesses can also do
everything process substitutions can, and are slightly more portable
(though the syntax for using them is not).

这也意味着大多数Android操作系统不允许进程替换,因为它们的shell通常基于mksh。

来自man bash

1
2
3
4
5
6
7
8
9
Process Substitution
   Process  substitution  allows  a process's input or output to be referred to using a filename.  It takes the form of <(list) or >(list).  The
   process list is run asynchronously, and its input or output appears as a filename.  This filename is passed as an  argument  to  the  current
   command  as  the result of the expansion.  If the >(list) form is used, writing to the file will provide input for list.  If the <(list) form
   is used, the file passed as an argument should be read to obtain the output of list.  Process substitution is supported on systems that  sup-
   port named pipes (FIFOs) or the /dev/fd method of naming open files.

   When  available, process substitution is performed simultaneously with parameter and variable expansion, command substitution, and arithmetic
   expansion.