ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

aaaa
11111-----

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

它似乎是一个纯文本。如果是二进制文件,则需要 this,因为 cat$(<someFile) 的结果将导致输出不完整(大小小于实际文件)。

Z
Zbyszek

在跨平台、最小公分母 sh 中,您使用:

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

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

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

bashzsh 中调用 cat 以 slurp 文件将被视为 Useless Use of Cat

请注意,不必引用命令替换来保留换行符。

请参阅:Bash Hacker's Wiki - Command substitution - Specialities


如果 config.txt 包含空格,value="`cat config.txt`"value="$(<config.txt)" 会不会更安全?
请注意,如上所述使用 cat 并不总是被视为对 cat 的无用使用。例如,< invalid-file 2>/dev/null 将导致无法路由到 /dev/null 的错误消息,而 cat invalid-file 2>/dev/null 会正确路由到 /dev/null
对于像我这样的新 shell 脚本编写者,请注意 cat 版本使用反引号,而不是单引号!希望这可以节省我花了半个小时才弄清楚的时间。
对于像我这样的新手:请注意,value=$(<config.txt) 是好的,但 value = $(<config.txt) 是坏的。注意那些空间。
@DejayClayton 使用 { var=$(<"$file"); } 2>/dev/null 禁用警告,请参阅 unix.stackexchange.com/questions/428500/…
b
brain

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

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

如果你想逐行阅读:

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

@brain:如果文件是 Config.cpp 并且包含反斜杠怎么办;双引号和引号?
您应该双引号 echo "$value" 中的变量。否则,shell 将对值执行空格标记化和通配符扩展。
@user2284570 使用 read -r 而不仅仅是 read - 总是,除非您特别需要您所暗示的奇怪的遗留行为。
C
Community

两个重要的陷阱

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

从命令扩展 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 包)...除了写入另一个文件外,我没有看到 bash 进程 <() 替换扩展的 POSIX 7 替代方案...

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


只感谢这个对我有用,因为我需要换行符。
@CiroSantilli:如果 FILE 是 Config.cpp 并且包含反斜杠怎么办?双引号和引号?
@user2284570 我不知道,但很容易找到:S="$(printf "\\\'\"")"; echo $S。输出:\'"。所以它有效=)
@CiroSantilli:在 5511 线上?你确定没有自动化的方法吗?
@user2284570 没看懂,哪里有5511行?陷阱来自 $() 扩展,我的示例显示 $() 扩展适用于 \'"
a
angelo.mastro

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


未被识别为 cmdlet、函数、脚本文件或可运行程序的名称。
严重地? <file_path> 表示 input the path of your file here
这将从多行文本文件中吞下换行符
L
Léa Gris

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

#!/usr/bin/env bash

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

printf '%s' "$value"

请注意:

最后一个换行符被保留。

通过重定向整个命令块,stderr 被静音到 /dev/null,但是如果需要处理读取错误情况,读取命令的返回状态会被保留。


d
dimo414

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

My approach 现在将 readprintf 内置函数的 -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

伟大的!我只是想知道,为什么不使用 _contents="${_contents}${_line}\n " 之类的东西来保留换行符?
您是在问 $'\n' 吗?这是必要的,否则您将附加文字 \ n 字符。您的代码块最后还有一个额外的空格,不确定这是否是故意的,但它会在随后的每一行缩进一个额外的空格。
P
Prakash D

您可以通过 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

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

chmod +x line.sh
./line.sh

您的 for 循环不会遍历行,而是遍历单词。在 /etc/passwd 的情况下,恰好每一行只包含一个单词。但是,其他文件可能每行包含多个单词。