如何从 stdin 读取多行输入到变量中,以及如何在 shell (sh,bash)中打印出一个变量?

我想做的是:

  1. 将多行输入从 stdin读入变量 A
  2. A上进行各种操作
  3. A引导到另一个命令而不丢失分隔符符号(\n\r\t等)

当前的问题是,我不能用 read命令读入它,因为它在换行处停止读入。

我可以用 cat读取 stdin,像这样:

my_var=`cat /dev/stdin`

但我不知道怎么打印出来。因此换行符、制表符和其他分隔符仍然存在。

我的示例脚本如下所示:

#!/usr/local/bin/bash


A=`cat /dev/stdin`


if [ ${#A} -eq 0 ]; then
exit 0
else
cat ${A} | /usr/local/sbin/nextcommand
fi
85636 次浏览

This is working for me:

myvar=`cat`


echo "$myvar"

The quotes around $myvar are important.

Yes it works for me too. Thanks.

myvar=`cat`

is the same as

myvar=`cat /dev/stdin`

Well yes. From the bash man page:

Enclosing characters in double quotes preserves the literal value of all characters within the quotes, with the exception of $, `, \, and, when history expansion is enabled, !. The characters $ and ` retain their special meaning within double quotes.

In Bash, there's an alternative way; man bash mentions:

The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).

$ myVar=$(</dev/stdin)
hello
this is test
$ echo "$myVar"
hello
this is test

tee does the job

#!/bin/bash
myVar=$(tee)

If you do care about preserving trailing newlines at the end of the output, use this:

myVar=$(cat; echo x)
myVar=${myVar%x}
printf %s "$myVar"

This uses the trick from here.

[updated]

This assignment will hang indefinitely if there is nothing in the pipe...

var="$(< /dev/stdin)"

We can prevent this though by doing a timeout read for the first character. If it times out, the return code will be greater than 128 and we'll know the STDIN pipe (a.k.a /dev/stdin) is empty.

Otherwise, we get the rest of STDIN by...

  • setting IFS to NULL for just the read command
  • turning off escapes with -r
  • eliminating read's delimiter with -d ''.
  • and finally, appending that to the character we got initially

Thus...

__=""
_stdin=""


read -N1 -t1 __  && {
(( $? <= 128 ))  && {
IFS= read -rd '' _stdin
_stdin="$__$_stdin"
}
}

This technique avoids using var="$(command ...)" Command Substitution which, by design, will always strip off any trailing newlines.

If Command Substitution is preferred, to preserve trailing newlines we can append one or more delimiter characters to the output inside the $() and then strip them off outside.

For example ( note ABC0 in first command and ${braces} in second )...

_stdin="$(awk '{print}; END {print "|||"}' /dev/stdin)"
_stdin="${_stdin%|||}"

Read can also be used setting option -d [DELIMITER], in which `input' DELIMITER is one character long. If you set -read d '' then it reads until null or all input.

Just remember Bash cannot hold null bytes in variables, anyways.

read -d'' myvar
echo "$myvar"

Obs: trailing newline bytes are not preserved, though..