如何在shell中读取一个文件到一个变量?

我想读取一个文件并将其保存在变量中,但我需要保留变量而不仅仅是打印出文件。 我该怎么做呢?我已经写了这个脚本,但它不是我所需要的:

#!/bin/sh
while read LINE
do
echo $LINE
done <$1
echo 11111-----------
echo $LINE

在我的脚本中,我可以将文件名作为参数,因此,如果文件包含“aaaa”,例如,它将打印出以下内容:

aaaa
11111-----
但是这只是把文件打印到屏幕上,我想把它保存到一个变量! 有什么简单的方法吗?< / p >
1230208 次浏览

如果你想把整个文件读入一个变量:

#!/bin/bash
value=`cat sources.xml`
echo $value

如果你想逐行阅读:

while read line; do
echo $line
done < file.txt

在跨平台游戏中,你可以使用最低公分母sh:

#!/bin/sh
value=`cat config.txt`
echo "$value"

bashzsh中,在不调用cat的情况下将整个文件读入变量:

#!/bin/bash
value=$(<config.txt)
echo "$value"

bashzsh中调用cat来获取文件将被视为无用地使用猫

注意,没有必要为命令替换加上引号来保留换行符。

看到:# EYZ0。

两个重要的陷阱

到目前为止,其他答案都忽略了这一点:

  1. 从命令展开中删除尾随换行符
  2. NUL字符删除

从命令展开中删除尾随换行符

这是一个问题:

value="$(cat config.txt)"

类型解决方案,但不是针对read的解决方案。

命令展开会移除尾随的换行:

S="$(printf "a\n")"
printf "$S" | od -tx1

输出:

0000000 61
0000001

这打破了从文件中读取的简单方法:

FILE="$(mktemp)"
printf "a\n\n" > "$FILE"
S="$(<"$FILE")"
printf "$S" | od -tx1
rm "$FILE"

POSIX解决方案:在命令展开部分附加一个额外的字符,然后将其删除:

S="$(cat $FILE; printf a)"
S="${S%a}"
printf "$S" | od -tx1

输出:

0000000 61 0a 0a
0000003

几乎POSIX的解决方案:ASCII编码。见下文。

NUL字符删除

# EYZ0。

这影响了扩展和read解决方案,我不知道有什么好的解决方案。

例子:

printf "a\0b" | od -tx1
S="$(printf "a\0b")"
printf "$S" | od -tx1

输出:

0000000 61 00 62
0000003


0000000 61 62
0000002

哈,我们的NUL不见了!

解决方法:

  • ASCII编码。见下文。

  • 使用bash扩展$""字面值:

    S=$"a\0b"
    printf "$S" | od -tx1
    

解决陷阱的方法

在变量中存储文件的uuencode base64编码版本,并在每次使用前解码:

FILE="$(mktemp)"
printf "a\0\n" > "$FILE"
S="$(uuencode -m "$FILE" /dev/stdout)"
uudecode -o /dev/stdout <(printf "$S") | od -tx1
rm "$FILE"

输出:

0000000 61 00 0a
0000003

uuencode和udecode是POSIX 7,但在Ubuntu 12.04中默认不是(sharutils包)…我没有看到POSIX 7的替代bash进程<()替换扩展除了写入另一个文件…

当然,这又慢又不方便,所以我想真正的答案是:如果输入文件可能包含NUL字符,就不要使用Bash。

作为Ciro Santilli指出使用命令替换将删除尾随换行符。他们添加尾随字符的变通方法很棒,但在使用了相当长的一段时间后,我决定需要一个根本不使用命令替换的解决方案。

我的方法现在使用readprintf内置的# EYZ2国旗,以便将stdin的内容直接读入变量。

# Reads stdin into a variable, accounting for trailing newlines. Avoids
# needing a subshell or command substitution.
# Note that NUL bytes are still unsupported, as Bash variables don't allow NULs.
# See https://stackoverflow.com/a/22607352/113632
read_input() {
# Use unusual variable names to avoid colliding with a variable name
# the user might pass in (notably "contents")
: "${1:?Must provide a variable to read into}"
if [[ "$1" == '_line' || "$1" == '_contents' ]]; then
echo "Cannot store contents to $1, use a different name." >&2
return 1
fi


local _line _contents=()
while IFS='' read -r _line; do
_contents+=("$_line"$'\n')
done
# include $_line once more to capture any content after the last newline
printf -v "$1" '%s' "${_contents[@]}" "$_line"
}

它支持带或不带换行符的输入。

使用示例:

$ read_input file_contents < /tmp/file
# $file_contents now contains the contents of /tmp/file

这对我有用: v = $(猫& lt; file_path>) echo $ v < /代码> < / p >

你可以通过for循环一次访问一行

#!/bin/bash -eu


#This script prints contents of /etc/passwd line by line


FILENAME='/etc/passwd'
I=0
for LN in $(cat $FILENAME)
do
echo "Line number $((I++)) -->  $LN"
done

将整个内容复制到File(比如line.sh);执行

chmod +x line.sh
./line.sh

在bash中,你可以像这样使用read:

#!/usr/bin/env bash


{ IFS= read -rd '' value <config.txt;} 2>/dev/null


printf '%s' "$value"

注意:

  • 保留最后一个换行符。

  • 通过重定向整个命令块,stderr被静默为/dev/null,但如果需要处理读错误条件,则read命令的返回状态被保留。

所有给定的解都非常慢,所以:

mapfile -d '' content </etc/passwd  # Read file into an array
content="${content[*]%$'\n'}"       # Remove trailing newline

更好地优化它,但我不能想到太多

更新:找到一个更快的方法

read -rd '' content </etc/passwd
这将返回退出代码1,所以如果你需要它 0:

read -rd '' content </etc/passwd || :