unix 如何在shell中将文件读入变量?

a1o7rhls  于 2022-11-23  发布在  Unix
关注(0)|答案(8)|浏览(121)

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

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

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

aaaa
11111-----

但这只是将文件打印到屏幕上,我想将其保存到一个变量中!有简单的方法吗?

3npbholx

3npbholx1#

在跨平台的最小公分母sh中,您用途:

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

bashzsh中,将整个文件读入变量而不调用cat

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

bashzsh中调用cat来抓取文件将被视为Useless Use of Cat
请注意,不必为保留换行符而引用命令替换。
参见:Bash Hacker 's Wiki -命令替换-专长。

avwztpqn

avwztpqn2#

如果要将整个文件读入变量:

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

如果您想逐行阅读:

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

798qvoo83#

两个重要的陷阱

到目前为止,其他答案都忽略了这一点:
1.从命令扩展中删除尾部换行符
1.删除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字符

There is no sane Bash way to store NUL characters in variables
这会影响扩展和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。

bjp0bcyl

bjp0bcyl4#

这对我很有效:v=$(cat <file_path>) echo $v

exdqitrt

exdqitrt5#

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

#!/usr/bin/env bash

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

printf '%s' "$value"

请注意:

  • 保留最后一个换行符。
  • 通过重定向整个命令块,将stderr静默为/dev/null,但如果需要处理读取错误条件,则保留读取命令的返回状态。
w8biq8rn

w8biq8rn6#

因为Ciro Santilli notes使用命令替换会丢弃尾部换行符。他们的解决方案添加尾部字符是伟大的,但在使用了相当长的一段时间后,我决定我需要一个解决方案,不使用命令替换在所有。
我的方法现在使用read沿着printf内置的-v flag,以便将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
ajsxfq5m

ajsxfq5m7#

所有给出的解都相当慢,因此:

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 || :
sqserrrh

sqserrrh8#

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

#!/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

将整个内容复制到文件(例如line.sh);执行

chmod +x line.sh
./line.sh

相关问题