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

kak*_*aka 424 unix shell sh

我想读取一个文件并将其保存在变量中,但我需要保留变量而不是打印出文件.我怎样才能做到这一点?我写过这个剧本,但它不是我需要的:

#!/bin/sh
while read LINE  
do  
  echo $LINE  
done <$1  
echo 11111-----------  
echo $LINE  
Run Code Online (Sandbox Code Playgroud)

在我的脚本中,我可以将文件名作为参数,因此,如果文件包含"aaaa",例如,它会打印出来:

aaaa
11111-----
Run Code Online (Sandbox Code Playgroud)

但这只是将文件打印到屏幕上,我想将其保存到变量中!是否有捷径可寻?

Ala*_*rez 940

在跨平台,最低通用分母中,sh您使用:

#!/bin/sh
value=`cat config.txt`
echo "$value"
Run Code Online (Sandbox Code Playgroud)

bash或中zsh,将整个文件读入变量而不调用cat:

#!/bin/bash
value=$(<config.txt)
echo "$value"
Run Code Online (Sandbox Code Playgroud)

调用catbashzsh以啜文件会被认为是猫无用的使用.

请注意,没有必要引用命令替换来保留换行符.

请参阅:Bash Hacker的Wiki - 命令替换 - 专业.

  • 不会``value ="`cat config.txt`"``和``value ="$(<config.txt)"``如果config.txt包含空格会更安全吗? (14认同)
  • 对于像我这样的新shell脚本编写者,请注意cat版本使用后退刻度,而不是单引号!希望这会让我花费半个小时才能弄明白. (14认同)
  • 请注意,使用上面的`cat`并不总是被认为是对`cat`的无用.例如,`<invalid-file 2>/dev/null`将导致无法路由到`/ dev/null`的错误消息,而`cat invalid-file 2>/dev/null`确实会得到正确路由到`/ dev/null`. (10认同)
  • 对于像我这样的新朋友:注意`value = $(<config.txt)`是好的,但`value = $(<config.txt)`是坏的.注意那些空间. (6认同)
  • 好吧,但它是bash,而不是sh; 它可能不适合所有情况. (4认同)
  • @DejayClayton使用`{var = $(&lt;“ $ file”); } 2&gt; / dev / null`以禁用警告,请参阅https://unix.stackexchange.com/questions/428500/how-to-make-bash-substitution-filename-silent/428529#428529 (2认同)
  • 反引号作为“最低级的通用示威者”正在迎合已经使用了数十年的炮弹。我会说对$ any * shell运行`$(...)`是因为`/ bin / sh`可以在任何地方工作。 (2认同)

bra*_*ain 85

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

#!/bin/bash
value=`cat sources.xml`
echo $value
Run Code Online (Sandbox Code Playgroud)

如果你想逐行阅读:

while read line; do    
    echo $line    
done < file.txt
Run Code Online (Sandbox Code Playgroud)

  • @ user2284570使用`read -r`而不仅仅是`read` - 总是如此,除非你特别要求你提到的奇怪的遗留行为. (3认同)
  • @brain:如果文件是Config.cpp并包含反斜杠,该怎么办?双引号和引号? (2认同)
  • 您应该在`echo“ $ value”`中双引号。否则,外壳程序将对该值执行空格标记化和通配符扩展。 (2认同)

Cir*_*四事件 72

两个重要的陷阱

到目前为止被其他答案忽略了:

  1. 从命令扩展中删除换行符
  2. NUL字符删除

从命令扩展中删除换行符

这是一个问题:

value="$(cat config.txt)"
Run Code Online (Sandbox Code Playgroud)

类型解决方案,但不适用于read基础解决方案

命令扩展删除尾随换行符:

S="$(printf "a\n")"
printf "$S" | od -tx1
Run Code Online (Sandbox Code Playgroud)

输出:

0000000 61
0000001
Run Code Online (Sandbox Code Playgroud)

这打破了从文件中读取的天真方法:

FILE="$(mktemp)"
printf "a\n\n" > "$FILE"
S="$(<"$FILE")"
printf "$S" | od -tx1
rm "$FILE"
Run Code Online (Sandbox Code Playgroud)

POSIX解决方法:在命令扩展中附加一个额外的char并稍后将其删除:

S="$(cat $FILE; printf a)"
S="${S%a}"
printf "$S" | od -tx1
Run Code Online (Sandbox Code Playgroud)

输出:

0000000 61 0a 0a
0000003
Run Code Online (Sandbox Code Playgroud)

几乎POSIX解决方法:ASCII编码.见下文.

NUL字符删除

没有合理的Bash方法在变量中存储NUL字符.

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

例:

printf "a\0b" | od -tx1
S="$(printf "a\0b")"
printf "$S" | od -tx1
Run Code Online (Sandbox Code Playgroud)

输出:

0000000 61 00 62
0000003

0000000 61 62
0000002
Run Code Online (Sandbox Code Playgroud)

哈,我们的NUL走了!

解决方法:

陷阱的解决方法

将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"
Run Code Online (Sandbox Code Playgroud)

输出:

0000000 61 00 0a
0000003
Run Code Online (Sandbox Code Playgroud)

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

当然,这很慢且不方便,所以我猜真正的答案是:如果输入文件可能包含NUL字符,请不要使用Bash.

  • 谢谢,只有这个为我工作,因为我需要换行. (2认同)

dim*_*414 6

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

我的方法read现在与printf内置-v标志一起使用,以便将 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"
}
Run Code Online (Sandbox Code Playgroud)

这支持带或不带尾随换行符的输入。

用法示例:

$ read_input file_contents < /tmp/file
# $file_contents now contains the contents of /tmp/file
Run Code Online (Sandbox Code Playgroud)


ang*_*tro 6

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


Léa*_*ris 6

使用 bash 你可以这样使用read

#!/usr/bin/env bash

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

printf '%s' "$value"
Run Code Online (Sandbox Code Playgroud)

请注意:

  • 保留最后一个换行符。

  • stderr被沉默到/dev/null通过重定向命令整体块,但是读命令的返回状态被保留,如果需要处理读取错误的条件。