我想读取一个文件并将其保存在变量中,但我需要保留变量而不仅仅是打印出文件。我怎样才能做到这一点?我已经编写了这个脚本,但它并不是我所需要的:
#!/bin/sh
while read LINE
do
echo $LINE
done <$1
echo 11111-----------
echo $LINE
在我的脚本中,我可以将文件名作为参数,因此,如果文件包含“aaaa”,例如,它会打印出:
aaaa
11111-----
但这只是将文件打印到屏幕上,我想将其保存到变量中!是否有捷径可寻?
在跨平台、最小公分母 sh
中,您使用:
#!/bin/sh
value=`cat config.txt`
echo "$value"
在 bash
或 zsh
中,将整个文件读入变量而不调用 cat
:
#!/bin/bash
value=$(<config.txt)
echo "$value"
在 bash
或 zsh
中调用 cat
以 slurp 文件将被视为 Useless Use of Cat。
请注意,不必引用命令替换来保留换行符。
请参阅:Bash Hacker's Wiki - Command substitution - Specialities。
如果要将整个文件读入变量:
#!/bin/bash
value=`cat sources.xml`
echo $value
如果你想逐行阅读:
while read line; do
echo $line
done < file.txt
echo "$value"
中的变量。否则,shell 将对值执行空格标记化和通配符扩展。
read -r
而不仅仅是 read
- 总是,除非您特别需要您所暗示的奇怪的遗留行为。
两个重要的陷阱
到目前为止,其他答案都忽略了这些:
从命令扩展 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。
S="$(printf "\\\'\"")"; echo $S
。输出:\'"
。所以它有效=)
$()
扩展,我的示例显示 $()
扩展适用于 \'"
。
这对我有用: v=$(cat <file_path>) echo $v
<file_path>
表示 input the path of your file here
使用 bash,您可以像这样使用 read
:
#!/usr/bin/env bash
{ IFS= read -rd '' value <config.txt;} 2>/dev/null
printf '%s' "$value"
请注意:
最后一个换行符被保留。
通过重定向整个命令块,stderr 被静音到 /dev/null,但是如果需要处理读取错误情况,读取命令的返回状态会被保留。
作为 Ciro Santilli notes 使用命令替换将删除尾随换行符。他们添加尾随字符的解决方法很棒,但是在使用了很长一段时间后,我决定我需要一个根本不使用命令替换的解决方案。
My approach 现在将 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
_contents="${_contents}${_line}\n "
之类的东西来保留换行符?
$'\n'
吗?这是必要的,否则您将附加文字 \
和 n
字符。您的代码块最后还有一个额外的空格,不确定这是否是故意的,但它会在随后的每一行缩进一个额外的空格。
您可以通过 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
的情况下,恰好每一行只包含一个单词。但是,其他文件可能每行包含多个单词。
value="`cat config.txt`"
和value="$(<config.txt)"
会不会更安全?cat
并不总是被视为对cat
的无用使用。例如,< invalid-file 2>/dev/null
将导致无法路由到/dev/null
的错误消息,而cat invalid-file 2>/dev/null
会正确路由到/dev/null
。value=$(<config.txt)
是好的,但value = $(<config.txt)
是坏的。注意那些空间。{ var=$(<"$file"); } 2>/dev/null
禁用警告,请参阅 unix.stackexchange.com/questions/428500/…